Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions crates/oxc_minifier/src/peephole/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,27 +185,55 @@ impl<'a> Normalize {
"undefined" if ident.is_global_reference(ctx.symbols()) => {
// `delete undefined` returns `false`
// `delete void 0` returns `true`
if matches!(ctx.parent(), Ancestor::UnaryExpressionArgument(e) if e.operator().is_delete())
{
if Self::is_unary_delete_ancestor(ctx.ancestors()) {
return None;
}
Some(ctx.ast.void_0(ident.span))
}
"Infinity" if ident.is_global_reference(ctx.symbols()) => {
// `delete Infinity` returns `false`
// `delete 1/0` returns `true`
if Self::is_unary_delete_ancestor(ctx.ancestors()) {
return None;
}
Some(ctx.ast.expression_numeric_literal(
ident.span,
f64::INFINITY,
None,
NumberBase::Decimal,
))
}
"NaN" if ident.is_global_reference(ctx.symbols()) => Some(
ctx.ast.expression_numeric_literal(ident.span, f64::NAN, None, NumberBase::Decimal),
),
"NaN" if ident.is_global_reference(ctx.symbols()) => {
// `delete NaN` returns `false`
// `delete 0/0` returns `true`
if Self::is_unary_delete_ancestor(ctx.ancestors()) {
return None;
}
Some(ctx.ast.expression_numeric_literal(
ident.span,
f64::NAN,
None,
NumberBase::Decimal,
))
}
_ => None,
}
}

fn is_unary_delete_ancestor<'t>(ancestors: impl Iterator<Item = Ancestor<'a, 't>>) -> bool {
for ancestor in ancestors {
match ancestor {
Ancestor::UnaryExpressionArgument(e) if e.operator().is_delete() => {
return true;
}
Ancestor::ParenthesizedExpressionExpression(_)
| Ancestor::SequenceExpressionExpressions(_) => {}
_ => return false,
}
}
false
}

fn convert_void_ident(e: &mut UnaryExpression<'a>, ctx: &mut TraverseCtx<'a>) {
debug_assert!(e.operator.is_void());
let Expression::Identifier(ident) = &e.argument else { return };
Expand Down
53 changes: 47 additions & 6 deletions crates/oxc_minifier/src/peephole/remove_dead_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,41 @@ impl<'a, 'b> PeepholeOptimizations {
///
/// * `access_value` - The expression that may need to be kept as indirect reference (`foo.bar` in the example above)
pub fn should_keep_indirect_access(access_value: &Expression<'a>, ctx: Ctx<'a, 'b>) -> bool {
matches!(
ctx.parent(),
Ancestor::CallExpressionCallee(_) | Ancestor::TaggedTemplateExpressionTag(_)
) && match access_value {
Expression::Identifier(id) => id.name == "eval" && ctx.is_global_reference(id),
match_member_expression!(Expression) => true,
match ctx.parent() {
Ancestor::CallExpressionCallee(_) | Ancestor::TaggedTemplateExpressionTag(_) => {
match access_value {
Expression::Identifier(id) => id.name == "eval" && ctx.is_global_reference(id),
match_member_expression!(Expression) => true,
_ => false,
}
}
Ancestor::UnaryExpressionArgument(unary) => match unary.operator() {
UnaryOperator::Typeof => {
// Example case: `typeof (0, foo)` (error) -> `typeof foo` (no error)
if let Expression::Identifier(id) = access_value {
ctx.is_global_reference(id)
} else {
false
}
}
UnaryOperator::Delete => {
match access_value {
// Example case: `delete (0, foo)` (no error) -> `delete foo` (error)
Expression::Identifier(_)
// Example case: `delete (0, foo.#a)` (no error) -> `delete foo.#a` (error)
| Expression::PrivateFieldExpression(_)
// Example case: `typeof (0, foo.bar)` (noop) -> `typeof foo.bar` (deletes bar)
| Expression::ComputedMemberExpression(_)
| Expression::StaticMemberExpression(_) => true,
// Example case: `typeof (0, foo?.bar)` (noop) -> `typeof foo?.bar` (deletes bar)
Expression::ChainExpression(chain) => {
matches!(&chain.expression, match_member_expression!(ChainElement))
}
_ => false,
}
}
_ => false,
},
_ => false,
}
}
Expand Down Expand Up @@ -686,6 +715,18 @@ mod test {
test("(true, true, foo.bar)();", "(0, foo.bar)();");
test("var foo; (true, foo.bar)();", "var foo; (0, foo.bar)();");
test("var foo; (true, true, foo.bar)();", "var foo; (0, foo.bar)();");

test("typeof (0, foo);", "foo");
test_same("v = typeof (0, foo);");
test("var foo; typeof (0, foo);", "var foo;");
test("var foo; v = typeof (0, foo);", "var foo; v = typeof foo");
test("typeof 0", "");

test_same("delete (0, foo);");
test_same("delete (0, foo.#bar);");
test_same("delete (0, foo.bar);");
test_same("delete (0, foo[bar]);");
test_same("delete (0, foo?.bar);");
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ fn test_unary_expressions() {

test("typeof 'foo'", false);
test_with_global_variables("typeof a", vec!["a".to_string()], false);
test_with_global_variables("typeof (0, a)", vec!["a".to_string()], true);
test("typeof foo()", true);

test("+0", false);
Expand Down
44 changes: 22 additions & 22 deletions crates/oxc_minifier/tests/peephole/esbuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1298,13 +1298,13 @@ fn test_flatten_values() {
// "function f(a) { let c = a.b; return c`${x}` }",
// "function f(a) { return (0, a.b)`${x}`;}",
// );
// test("return typeof (123, x)", "return typeof (0, x);");
test("return typeof (123, x)", "return typeof (0, x);");
test("return typeof (123, x.y)", "return typeof x.y;");
test("return typeof (123, x); var x", "return typeof x;var x;");
// test("return typeof (true && x)", "return typeof (0, x);");
test("return typeof (true && x)", "return typeof (0, x);");
test("return typeof (true && x.y)", "return typeof x.y;");
test("return typeof (true && x); var x", "return typeof x;var x;");
// test("return typeof (false || x)", "return typeof (0, x);");
test("return typeof (false || x)", "return typeof (0, x);");
test("return typeof (false || x.y)", "return typeof x.y;");
test("return typeof (false || x); var x", "return typeof x;var x;");
test("return typeof x !== 'undefined'", "return typeof x < 'u';");
Expand Down Expand Up @@ -1463,11 +1463,11 @@ fn test_remove_dead_expr() {
test("delete x", "delete x;");
test("typeof x", "");
test("typeof x()", "x();");
// test("typeof (0, x)", "x;");
// test("typeof (0 || x)", "x;");
// test("typeof (1 && x)", "x;");
// test("typeof (1 ? x : 0)", "x;");
// test("typeof (0 ? 1 : x)", "x;");
test("typeof (0, x)", "x;");
test("typeof (0 || x)", "x;");
test("typeof (1 && x)", "x;");
test("typeof (1 ? x : 0)", "x;");
test("typeof (0 ? 1 : x)", "x;");
test("a + b", "a + b;");
test("a - b", "a - b;");
test("a * b", "a * b;");
Expand Down Expand Up @@ -1551,22 +1551,22 @@ fn test_remove_dead_expr() {
test("delete (x[y])", "delete x[y];");
test("delete (x?.y)", "delete x?.y;");
test("delete (x?.[y])", "delete x?.[y];");
// test("delete (2, x)", "delete (0, x);");
// test("delete (2, x); var x", "delete (0, x);var x;");
// test("delete (2, x.y)", "delete (0, x.y);");
// test("delete (2, x[y])", "delete (0, x[y]);");
// test("delete (2, x?.y)", "delete (0, x?.y);");
// test("delete (2, x?.[y])", "delete (0, x?.[y]);");
// test("delete (true && x)", "delete (0, x);");
// test("delete (false || x)", "delete (0, x);");
// test("delete (null ?? x)", "delete (0, x);");
// test("delete (1 ? x : 2)", "delete (0, x);");
// test("delete (0 ? 1 : x)", "delete (0, x);");
test("delete (2, x)", "delete (0, x);");
test("delete (2, x); var x", "delete (0, x);var x;");
test("delete (2, x.y)", "delete (0, x.y);");
test("delete (2, x[y])", "delete (0, x[y]);");
test("delete (2, x?.y)", "delete (0, x?.y);");
test("delete (2, x?.[y])", "delete (0, x?.[y]);");
test("delete (true && x)", "delete (0, x);");
test("delete (false || x)", "delete (0, x);");
test("delete (null ?? x)", "delete (0, x);");
test("delete (1 ? x : 2)", "delete (0, x);");
test("delete (0 ? 1 : x)", "delete (0, x);");
test("delete (NaN)", "delete NaN;");
// test("delete (Infinity)", "delete Infinity;");
test("delete (Infinity)", "delete Infinity;");
test("delete (-Infinity)", "delete -Infinity;");
// test("delete (1, NaN)", "delete (0, NaN);");
// test("delete (1, Infinity)", "delete (0, Infinity);");
test("delete (1, NaN)", "delete (0, NaN);");
test("delete (1, Infinity)", "delete (0, Infinity);");
test("delete (1, -Infinity)", "delete -Infinity;");
test("foo ? 1 : 2", "foo;");
test("foo ? 1 : bar", "foo || bar;");
Expand Down
Loading