mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
fix(minifier): void 0 equals to undefined (#8673)
This commit is contained in:
parent
1bb2539d64
commit
878ce10296
3 changed files with 28 additions and 10 deletions
|
|
@ -82,6 +82,12 @@ impl<'a> Ctx<'a, '_> {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
/// If two expressions are equal.
|
||||||
|
/// Special case `undefined` == `void 0`
|
||||||
|
pub fn expr_eq(self, a: &Expression<'a>, b: &Expression<'a>) -> bool {
|
||||||
|
use oxc_span::cmp::ContentEq;
|
||||||
|
a.content_eq(b) || (self.is_expression_undefined(a) && self.is_expression_undefined(b))
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L2641
|
// https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L2641
|
||||||
pub fn string_to_equivalent_number_value(s: &str) -> Option<f64> {
|
pub fn string_to_equivalent_number_value(s: &str) -> Option<f64> {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
loop {
|
loop {
|
||||||
let mut local_change = false;
|
let mut local_change = false;
|
||||||
Self::try_replace_if(stmts, &mut local_change, ctx);
|
self.try_replace_if(stmts, &mut local_change, ctx);
|
||||||
if local_change {
|
if local_change {
|
||||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||||
changed = local_change;
|
changed = local_change;
|
||||||
|
|
@ -273,6 +273,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_replace_if(
|
fn try_replace_if(
|
||||||
|
&mut self,
|
||||||
stmts: &mut Vec<'a, Statement<'a>>,
|
stmts: &mut Vec<'a, Statement<'a>>,
|
||||||
changed: &mut bool,
|
changed: &mut bool,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
|
@ -301,13 +302,14 @@ impl<'a> PeepholeOptimizations {
|
||||||
let mut if_stmt = if_stmt.unbox();
|
let mut if_stmt = if_stmt.unbox();
|
||||||
let consequent = Self::get_block_return_expression(&mut if_stmt.consequent, ctx);
|
let consequent = Self::get_block_return_expression(&mut if_stmt.consequent, ctx);
|
||||||
let alternate = Self::take_return_argument(&mut stmts[i + 1], ctx);
|
let alternate = Self::take_return_argument(&mut stmts[i + 1], ctx);
|
||||||
let argument = Self::minimize_conditional(
|
let mut argument = Self::minimize_conditional(
|
||||||
if_stmt.span,
|
if_stmt.span,
|
||||||
if_stmt.test,
|
if_stmt.test,
|
||||||
consequent,
|
consequent,
|
||||||
alternate,
|
alternate,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
|
self.minimize_conditions_exit_expression(&mut argument, ctx);
|
||||||
stmts[i] = ctx.ast.statement_return(if_stmt.span, Some(argument));
|
stmts[i] = ctx.ast.statement_return(if_stmt.span, Some(argument));
|
||||||
*changed = true;
|
*changed = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -486,7 +488,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
|
|
||||||
// "a ? b ? c : d : d" => "a && b ? c : d"
|
// "a ? b ? c : d : d" => "a && b ? c : d"
|
||||||
if let Expression::ConditionalExpression(consequent) = &mut expr.consequent {
|
if let Expression::ConditionalExpression(consequent) = &mut expr.consequent {
|
||||||
if consequent.alternate.content_eq(&expr.alternate) {
|
if Ctx(ctx).expr_eq(&consequent.alternate, &expr.alternate) {
|
||||||
return Some(ctx.ast.expression_conditional(
|
return Some(ctx.ast.expression_conditional(
|
||||||
expr.span,
|
expr.span,
|
||||||
ctx.ast.expression_logical(
|
ctx.ast.expression_logical(
|
||||||
|
|
@ -503,7 +505,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
|
|
||||||
// "a ? b : c ? b : d" => "a || c ? b : d"
|
// "a ? b : c ? b : d" => "a || c ? b : d"
|
||||||
if let Expression::ConditionalExpression(alternate) = &mut expr.alternate {
|
if let Expression::ConditionalExpression(alternate) = &mut expr.alternate {
|
||||||
if alternate.consequent.content_eq(&expr.consequent) {
|
if Ctx(ctx).expr_eq(&alternate.consequent, &expr.consequent) {
|
||||||
return Some(ctx.ast.expression_conditional(
|
return Some(ctx.ast.expression_conditional(
|
||||||
expr.span,
|
expr.span,
|
||||||
ctx.ast.expression_logical(
|
ctx.ast.expression_logical(
|
||||||
|
|
@ -521,7 +523,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
// "a ? c : (b, c)" => "(a || b), c"
|
// "a ? c : (b, c)" => "(a || b), c"
|
||||||
if let Expression::SequenceExpression(alternate) = &mut expr.alternate {
|
if let Expression::SequenceExpression(alternate) = &mut expr.alternate {
|
||||||
if alternate.expressions.len() == 2
|
if alternate.expressions.len() == 2
|
||||||
&& alternate.expressions[1].content_eq(&expr.consequent)
|
&& Ctx(ctx).expr_eq(&alternate.expressions[1], &expr.consequent)
|
||||||
{
|
{
|
||||||
return Some(ctx.ast.expression_sequence(
|
return Some(ctx.ast.expression_sequence(
|
||||||
expr.span,
|
expr.span,
|
||||||
|
|
@ -541,7 +543,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
// "a ? (b, c) : c" => "(a && b), c"
|
// "a ? (b, c) : c" => "(a && b), c"
|
||||||
if let Expression::SequenceExpression(consequent) = &mut expr.consequent {
|
if let Expression::SequenceExpression(consequent) = &mut expr.consequent {
|
||||||
if consequent.expressions.len() == 2
|
if consequent.expressions.len() == 2
|
||||||
&& consequent.expressions[1].content_eq(&expr.alternate)
|
&& Ctx(ctx).expr_eq(&consequent.expressions[1], &expr.alternate)
|
||||||
{
|
{
|
||||||
return Some(ctx.ast.expression_sequence(
|
return Some(ctx.ast.expression_sequence(
|
||||||
expr.span,
|
expr.span,
|
||||||
|
|
@ -561,7 +563,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
// "a ? b || c : c" => "(a && b) || c"
|
// "a ? b || c : c" => "(a && b) || c"
|
||||||
if let Expression::LogicalExpression(logical_expr) = &mut expr.consequent {
|
if let Expression::LogicalExpression(logical_expr) = &mut expr.consequent {
|
||||||
if logical_expr.operator == LogicalOperator::Or
|
if logical_expr.operator == LogicalOperator::Or
|
||||||
&& logical_expr.right.content_eq(&expr.alternate)
|
&& Ctx(ctx).expr_eq(&logical_expr.right, &expr.alternate)
|
||||||
{
|
{
|
||||||
return Some(ctx.ast.expression_logical(
|
return Some(ctx.ast.expression_logical(
|
||||||
expr.span,
|
expr.span,
|
||||||
|
|
@ -580,7 +582,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
// "a ? c : b && c" => "(a || b) && c"
|
// "a ? c : b && c" => "(a || b) && c"
|
||||||
if let Expression::LogicalExpression(logical_expr) = &mut expr.alternate {
|
if let Expression::LogicalExpression(logical_expr) = &mut expr.alternate {
|
||||||
if logical_expr.operator == LogicalOperator::And
|
if logical_expr.operator == LogicalOperator::And
|
||||||
&& logical_expr.right.content_eq(&expr.consequent)
|
&& Ctx(ctx).expr_eq(&logical_expr.right, &expr.consequent)
|
||||||
{
|
{
|
||||||
return Some(ctx.ast.expression_logical(
|
return Some(ctx.ast.expression_logical(
|
||||||
expr.span,
|
expr.span,
|
||||||
|
|
@ -605,7 +607,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
{
|
{
|
||||||
if consequent.arguments.len() == alternate.arguments.len()
|
if consequent.arguments.len() == alternate.arguments.len()
|
||||||
&& !Ctx(ctx).is_global_reference(test)
|
&& !Ctx(ctx).is_global_reference(test)
|
||||||
&& consequent.callee.content_eq(&alternate.callee)
|
&& Ctx(ctx).expr_eq(&consequent.callee, &alternate.callee)
|
||||||
&& consequent
|
&& consequent
|
||||||
.arguments
|
.arguments
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -673,7 +675,7 @@ impl<'a> PeepholeOptimizations {
|
||||||
|
|
||||||
// TODO: Try using the "??" or "?." operators
|
// TODO: Try using the "??" or "?." operators
|
||||||
|
|
||||||
if expr.alternate.content_eq(&expr.consequent) {
|
if Ctx(ctx).expr_eq(&expr.alternate, &expr.consequent) {
|
||||||
// TODO:
|
// TODO:
|
||||||
// "/* @__PURE__ */ a() ? b : b" => "b"
|
// "/* @__PURE__ */ a() ? b : b" => "b"
|
||||||
// if ctx.ExprCanBeRemovedIfUnused(test) {
|
// if ctx.ExprCanBeRemovedIfUnused(test) {
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,16 @@ fn integration() {
|
||||||
"(!(foo instanceof Var) || open || (que || !(foo && bar)) && baz()) && (arg0 = null);",
|
"(!(foo instanceof Var) || open || (que || !(foo && bar)) && baz()) && (arg0 = null);",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"function foo() {
|
||||||
|
if (value === null || Array.isArray(value))
|
||||||
|
return undefined;
|
||||||
|
return isTimeDisabled === null || isTimeDisabled === void 0 ? void 0 : isTimeDisabled(value);
|
||||||
|
}",
|
||||||
|
"function foo() {
|
||||||
|
return value === null || Array.isArray(value) || isTimeDisabled == null ? void 0 : isTimeDisabled(value);
|
||||||
|
}");
|
||||||
|
|
||||||
test_same("a && (b && (c && (d && (e && (f && (g && (h && i && j && k && l && m && n && o && p && q && r && s && t && u && v && w && x && y && z)))))))");
|
test_same("a && (b && (c && (d && (e && (f && (g && (h && i && j && k && l && m && n && o && p && q && r && s && t && u && v && w && x && y && z)))))))");
|
||||||
|
|
||||||
test(
|
test(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue