mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(minifier): fold String() -> '', Number() -> false (#8274)
This commit is contained in:
parent
aea95519db
commit
0e3b79a65b
1 changed files with 80 additions and 76 deletions
|
|
@ -655,7 +655,7 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax {
|
||||||
call_expr: &mut CallExpression<'a>,
|
call_expr: &mut CallExpression<'a>,
|
||||||
ctx: Ctx<'a, 'b>,
|
ctx: Ctx<'a, 'b>,
|
||||||
) -> Option<Expression<'a>> {
|
) -> Option<Expression<'a>> {
|
||||||
if call_expr.optional || call_expr.arguments.len() != 1 {
|
if call_expr.optional || call_expr.arguments.len() >= 2 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let Expression::Identifier(ident) = &call_expr.callee else { return None };
|
let Expression::Identifier(ident) = &call_expr.callee else { return None };
|
||||||
|
|
@ -664,71 +664,73 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let args = &mut call_expr.arguments;
|
let args = &mut call_expr.arguments;
|
||||||
if args.len() != 1 {
|
let arg = match args.get_mut(0) {
|
||||||
return None;
|
None => None,
|
||||||
}
|
Some(arg) => Some(arg.as_expression_mut()?),
|
||||||
let arg = args[0].as_expression_mut()?;
|
};
|
||||||
if !ctx.is_global_reference(ident) {
|
if !ctx.is_global_reference(ident) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let span = call_expr.span;
|
||||||
match name {
|
match name {
|
||||||
// `Boolean(a)` -> `!!(a)`
|
// `Boolean(a)` -> `!!(a)`
|
||||||
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-boolean-constructor-boolean-value
|
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-boolean-constructor-boolean-value
|
||||||
// and
|
// and
|
||||||
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-logical-not-operator-runtime-semantics-evaluation
|
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-logical-not-operator-runtime-semantics-evaluation
|
||||||
"Boolean" => {
|
"Boolean" => match arg {
|
||||||
|
None => Some(ctx.ast.expression_boolean_literal(span, false)),
|
||||||
|
Some(arg) => {
|
||||||
if let Expression::UnaryExpression(unary_expr) = arg {
|
if let Expression::UnaryExpression(unary_expr) = arg {
|
||||||
if unary_expr.operator == UnaryOperator::LogicalNot {
|
if unary_expr.operator == UnaryOperator::LogicalNot {
|
||||||
return Some(ctx.ast.move_expression(arg));
|
return Some(ctx.ast.move_expression(arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(
|
Some(ctx.ast.expression_unary(
|
||||||
ctx.ast.expression_unary(
|
span,
|
||||||
call_expr.span,
|
|
||||||
UnaryOperator::LogicalNot,
|
UnaryOperator::LogicalNot,
|
||||||
ctx.ast.expression_unary(
|
ctx.ast.expression_unary(
|
||||||
call_expr.span,
|
span,
|
||||||
UnaryOperator::LogicalNot,
|
UnaryOperator::LogicalNot,
|
||||||
ctx.ast.move_expression(
|
ctx.ast.move_expression(arg),
|
||||||
call_expr
|
|
||||||
.arguments
|
|
||||||
.get_mut(0)
|
|
||||||
.and_then(|arg| arg.as_expression_mut())?,
|
|
||||||
),
|
),
|
||||||
),
|
))
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
"String" => {
|
"String" => {
|
||||||
|
match arg {
|
||||||
|
// `String()` -> `''`
|
||||||
|
None => Some(ctx.ast.expression_string_literal(span, "", None)),
|
||||||
// `String(a)` -> `'' + (a)`
|
// `String(a)` -> `'' + (a)`
|
||||||
|
Some(arg) => {
|
||||||
if !matches!(arg, Expression::Identifier(_) | Expression::CallExpression(_))
|
if !matches!(arg, Expression::Identifier(_) | Expression::CallExpression(_))
|
||||||
&& !arg.is_literal()
|
&& !arg.is_literal()
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(ctx.ast.expression_binary(
|
Some(ctx.ast.expression_binary(
|
||||||
call_expr.span,
|
span,
|
||||||
ctx.ast.expression_string_literal(SPAN, "", None),
|
ctx.ast.expression_string_literal(SPAN, "", None),
|
||||||
BinaryOperator::Addition,
|
BinaryOperator::Addition,
|
||||||
ctx.ast.move_expression(arg),
|
ctx.ast.move_expression(arg),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
"Number" => {
|
}
|
||||||
let number = arg.to_number()?;
|
}
|
||||||
Some(ctx.ast.expression_numeric_literal(
|
"Number" => Some(ctx.ast.expression_numeric_literal(
|
||||||
call_expr.span,
|
span,
|
||||||
number,
|
match arg {
|
||||||
|
None => 0.0,
|
||||||
|
Some(arg) => arg.to_number()?,
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
NumberBase::Decimal,
|
NumberBase::Decimal,
|
||||||
))
|
)),
|
||||||
}
|
|
||||||
// `BigInt(1n)` -> `1n`
|
// `BigInt(1n)` -> `1n`
|
||||||
"BigInt" => {
|
"BigInt" => match arg {
|
||||||
if !matches!(arg, Expression::BigIntLiteral(_)) {
|
None => None,
|
||||||
return None;
|
Some(arg) => matches!(arg, Expression::BigIntLiteral(_))
|
||||||
}
|
.then(|| ctx.ast.move_expression(arg)),
|
||||||
Some(ctx.ast.move_expression(arg))
|
},
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1241,32 +1243,6 @@ mod test {
|
||||||
test_same("goog.bind(f.m).call(g)");
|
test_same("goog.bind(f.m).call(g)");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_simple_function_call2() {
|
|
||||||
test("var a = Boolean(true)", "var a = !0");
|
|
||||||
// Don't fold the existence check to preserve behavior
|
|
||||||
test("var a = Boolean?.(true)", "var a = Boolean?.(!0)");
|
|
||||||
|
|
||||||
test("var a = Boolean(false)", "var a = !1");
|
|
||||||
// Don't fold the existence check to preserve behavior
|
|
||||||
test("var a = Boolean?.(false)", "var a = Boolean?.(!1)");
|
|
||||||
|
|
||||||
test("var a = Boolean(1)", "var a = !!1");
|
|
||||||
// Don't fold the existence check to preserve behavior
|
|
||||||
test_same("var a = Boolean?.(1)");
|
|
||||||
|
|
||||||
test("var a = Boolean(x)", "var a = !!x");
|
|
||||||
// Don't fold the existence check to preserve behavior
|
|
||||||
test_same("var a = Boolean?.(x)");
|
|
||||||
|
|
||||||
test("var a = Boolean({})", "var a = !!{}");
|
|
||||||
// Don't fold the existence check to preserve behavior
|
|
||||||
test_same("var a = Boolean?.({})");
|
|
||||||
|
|
||||||
test_same("var a = Boolean()");
|
|
||||||
test_same("var a = Boolean(!0, !1);");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn test_rotate_associative_operators() {
|
fn test_rotate_associative_operators() {
|
||||||
|
|
@ -1432,8 +1408,35 @@ mod test {
|
||||||
test("f(a, ...[])", "f(a)");
|
test("f(a, ...[])", "f(a)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fold_boolean_constructor() {
|
||||||
|
test("var a = Boolean(true)", "var a = !0");
|
||||||
|
// Don't fold the existence check to preserve behavior
|
||||||
|
test("var a = Boolean?.(true)", "var a = Boolean?.(!0)");
|
||||||
|
|
||||||
|
test("var a = Boolean(false)", "var a = !1");
|
||||||
|
// Don't fold the existence check to preserve behavior
|
||||||
|
test("var a = Boolean?.(false)", "var a = Boolean?.(!1)");
|
||||||
|
|
||||||
|
test("var a = Boolean(1)", "var a = !!1");
|
||||||
|
// Don't fold the existence check to preserve behavior
|
||||||
|
test_same("var a = Boolean?.(1)");
|
||||||
|
|
||||||
|
test("var a = Boolean(x)", "var a = !!x");
|
||||||
|
// Don't fold the existence check to preserve behavior
|
||||||
|
test_same("var a = Boolean?.(x)");
|
||||||
|
|
||||||
|
test("var a = Boolean({})", "var a = !!{}");
|
||||||
|
// Don't fold the existence check to preserve behavior
|
||||||
|
test_same("var a = Boolean?.({})");
|
||||||
|
|
||||||
|
test("var a = Boolean()", "var a = false;");
|
||||||
|
test_same("var a = Boolean(!0, !1);");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fold_string_constructor() {
|
fn test_fold_string_constructor() {
|
||||||
|
test("String()", "''");
|
||||||
test("var a = String(23)", "var a = '' + 23");
|
test("var a = String(23)", "var a = '' + 23");
|
||||||
// Don't fold the existence check to preserve behavior
|
// Don't fold the existence check to preserve behavior
|
||||||
test_same("var a = String?.(23)");
|
test_same("var a = String?.(23)");
|
||||||
|
|
@ -1448,7 +1451,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fold_number_constructor() {
|
fn test_fold_number_constructor() {
|
||||||
test("Number(0)", "0");
|
test("Number()", "0");
|
||||||
test("Number(true)", "1");
|
test("Number(true)", "1");
|
||||||
test("Number(false)", "0");
|
test("Number(false)", "0");
|
||||||
test("Number('foo')", "NaN");
|
test("Number('foo')", "NaN");
|
||||||
|
|
@ -1457,6 +1460,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fold_big_int_constructor() {
|
fn test_fold_big_int_constructor() {
|
||||||
test("BigInt(1n)", "1n");
|
test("BigInt(1n)", "1n");
|
||||||
|
test_same("BigInt()");
|
||||||
test_same("BigInt(1)");
|
test_same("BigInt(1)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue