feat(minifier): constant fold undefined?.bar -> undefined (#8075)

This commit is contained in:
Boshen 2024-12-23 15:20:36 +00:00
parent d84d60a3a9
commit 5397fe978a
2 changed files with 46 additions and 5 deletions

View file

@ -17,20 +17,28 @@ pub enum ValueType {
}
impl ValueType {
pub fn is_undefined(self) -> bool {
self == Self::Undefined
}
pub fn is_null(self) -> bool {
self == Self::Null
}
pub fn is_string(self) -> bool {
matches!(self, Self::String)
self == Self::String
}
pub fn is_number(self) -> bool {
matches!(self, Self::Number)
self == Self::Number
}
pub fn is_bigint(self) -> bool {
matches!(self, Self::BigInt)
self == Self::BigInt
}
pub fn is_boolean(self) -> bool {
matches!(self, Self::Boolean)
self == Self::Boolean
}
}

View file

@ -1,6 +1,6 @@
use oxc_ast::ast::*;
use oxc_ecmascript::{
constant_evaluation::{ConstantEvaluation, ValueType},
constant_evaluation::{ConstantEvaluation, ConstantValue, ValueType},
side_effects::MayHaveSideEffects,
};
use oxc_span::{GetSpan, SPAN};
@ -53,6 +53,7 @@ impl<'a> Traverse<'a> for PeepholeFoldConstants {
}
// TODO: return tryFoldGetProp(subtree);
Expression::LogicalExpression(e) => Self::try_fold_logical_expression(e, ctx),
Expression::ChainExpression(e) => Self::try_fold_optional_chain(e, ctx),
// TODO: tryFoldGetElem
// TODO: tryFoldAssign
_ => None,
@ -106,6 +107,20 @@ impl<'a, 'b> PeepholeFoldConstants {
}
}
fn try_fold_optional_chain(
chain_expr: &mut ChainExpression<'a>,
ctx: Ctx<'a, 'b>,
) -> Option<Expression<'a>> {
let member_expr = chain_expr.expression.as_member_expression()?;
if !member_expr.optional() {
return None;
}
let object = member_expr.object();
let ty = ValueType::from(object);
(ty.is_null() || ty.is_undefined())
.then(|| ctx.value_to_expr(chain_expr.span, ConstantValue::Undefined))
}
/// Try to fold a AND / OR node.
///
/// port from [closure-compiler](https://github.com/google/closure-compiler/blob/09094b551915a6487a980a783831cba58b5739d1/src/com/google/javascript/jscomp/PeepholeFoldConstants.java#L587)
@ -1149,6 +1164,24 @@ mod test {
test_same("void x()");
}
#[test]
fn test_fold_opt_chain() {
// can't fold when optional part may execute
test_same("a = x?.y");
test_same("a = x?.()");
// fold args of optional call
test("x = foo() ?. (true && bar())", "x = foo() ?.(bar())");
test("a() ?. (1 ?? b())", "a() ?. (1)");
// test("({a})?.a.b.c.d()?.x.y.z", "a.b.c.d()?.x.y.z");
test("x = undefined?.y", "x = void 0");
test("x = null?.y", "x = void 0");
test("x = undefined?.[foo]", "x = void 0");
test("x = null?.[foo]", "x = void 0");
}
#[test]
fn test_fold_bitwise_op() {
test("x = 1 & 1", "x = 1");