feat(minifier): fold literal object constructors on window (#6379)

Fold literal object constructors on window: `window.Object()` -> `{}` or
`window.Object?.()` -> `Object?.()`


92d42692bb/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java (L98-L99)
This commit is contained in:
dalaoshu 2024-10-09 09:04:43 +08:00 committed by GitHub
parent 6e3224d5fa
commit 13b0b0b07e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -106,6 +106,11 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
self.changed = true;
}
}
Expression::ChainExpression(chain_expr) => {
if let ChainElement::CallExpression(call_expr) = &mut chain_expr.expression {
self.try_fold_chain_call_expression(call_expr, ctx);
}
}
_ => {}
}
}
@ -349,6 +354,11 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
}
}
fn is_window_object(expr: &Expression) -> bool {
expr.as_member_expression()
.is_some_and(|mem_expr| mem_expr.is_specific_member_access("window", "Object"))
}
fn try_fold_new_expression(
&mut self,
new_expr: &mut NewExpression<'a>,
@ -356,7 +366,8 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
) -> Option<Expression<'a>> {
// `new Object` -> `{}`
if new_expr.arguments.is_empty()
&& new_expr.callee.is_global_reference_name("Object", ctx.symbols())
&& (new_expr.callee.is_global_reference_name("Object", ctx.symbols())
|| Self::is_window_object(&new_expr.callee))
{
Some(ctx.ast.expression_object(new_expr.span, ctx.ast.vec(), None))
} else if new_expr.callee.is_global_reference_name("Array", ctx.symbols()) {
@ -413,7 +424,8 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
) -> Option<Expression<'a>> {
// `Object()` -> `{}`
if call_expr.arguments.is_empty()
&& call_expr.callee.is_global_reference_name("Object", ctx.symbols())
&& (call_expr.callee.is_global_reference_name("Object", ctx.symbols())
|| Self::is_window_object(&call_expr.callee))
{
Some(ctx.ast.expression_object(call_expr.span, ctx.ast.vec(), None))
} else if call_expr.callee.is_global_reference_name("Array", ctx.symbols()) {
@ -462,6 +474,19 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
}
}
fn try_fold_chain_call_expression(
&mut self,
call_expr: &mut CallExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
// `window.Object?.()` -> `Object?.()`
if call_expr.arguments.is_empty() && Self::is_window_object(&call_expr.callee) {
call_expr.callee =
ctx.ast.expression_identifier_reference(call_expr.callee.span(), "Object");
self.changed = true;
}
}
/// returns an `Array()` constructor call with zero, one, or more arguments, copying from the input
fn array_constructor_call(
&self,
@ -569,6 +594,21 @@ mod test {
test_same("x = (function f(){function Object(){this.x=4}return new Object();})();");
}
#[test]
fn test_fold_literal_object_constructors_on_window() {
test("x = new window.Object", "x = ({})");
test("x = new window.Object()", "x = ({})");
// Mustn't fold optional chains
test("x = window.Object()", "x = ({})");
test("x = window.Object?.()", "x = Object?.()");
test(
"x = (function f(){function Object(){this.x=4};return new window.Object;})();",
"x = (function f(){function Object(){this.x=4}return {};})();",
);
}
#[test]
fn test_fold_literal_array_constructors() {
test("x = new Array", "x = []");