mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(minifier): finish implementing folding object expressions (#6586)
This commit is contained in:
parent
15c04e5cbb
commit
071e5643f3
2 changed files with 102 additions and 5 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use oxc_ast::ast::{Expression, ForStatementLeft, UnaryExpression};
|
use oxc_ast::ast::{Expression, ForStatementLeft, PropertyKey, UnaryExpression};
|
||||||
|
|
||||||
use super::check_for_state_change::CheckForStateChange;
|
use super::check_for_state_change::CheckForStateChange;
|
||||||
|
|
||||||
|
|
@ -19,3 +19,4 @@ where
|
||||||
impl<'a, 'b> MayHaveSideEffects<'a, 'b> for Expression<'a> {}
|
impl<'a, 'b> MayHaveSideEffects<'a, 'b> for Expression<'a> {}
|
||||||
impl<'a, 'b> MayHaveSideEffects<'a, 'b> for UnaryExpression<'a> {}
|
impl<'a, 'b> MayHaveSideEffects<'a, 'b> for UnaryExpression<'a> {}
|
||||||
impl<'a, 'b> MayHaveSideEffects<'a, 'b> for ForStatementLeft<'a> {}
|
impl<'a, 'b> MayHaveSideEffects<'a, 'b> for ForStatementLeft<'a> {}
|
||||||
|
impl<'a, 'b> MayHaveSideEffects<'a, 'b> for PropertyKey<'a> {}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use oxc_allocator::Vec;
|
use oxc_allocator::Vec;
|
||||||
use oxc_ast::{ast::*, Visit};
|
use oxc_ast::{ast::*, Visit};
|
||||||
use oxc_ecmascript::constant_evaluation::{ConstantEvaluation, IsLiteralValue};
|
use oxc_ecmascript::constant_evaluation::{ConstantEvaluation, IsLiteralValue};
|
||||||
|
use oxc_ecmascript::side_effects::MayHaveSideEffects;
|
||||||
use oxc_span::SPAN;
|
use oxc_span::SPAN;
|
||||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||||
|
|
||||||
|
|
@ -253,8 +254,9 @@ impl<'a, 'b> PeepholeRemoveDeadCode {
|
||||||
.then(|| Some(ctx.ast.statement_empty(SPAN)))
|
.then(|| Some(ctx.ast.statement_empty(SPAN)))
|
||||||
.unwrap_or_else(|| match &mut stmt.expression {
|
.unwrap_or_else(|| match &mut stmt.expression {
|
||||||
Expression::ArrayExpression(expr) => Self::try_fold_array_expression(expr, ctx),
|
Expression::ArrayExpression(expr) => Self::try_fold_array_expression(expr, ctx),
|
||||||
// TODO: handle object expression
|
Expression::ObjectExpression(object_expr) => {
|
||||||
Expression::ObjectExpression(_object_expr) => None,
|
Self::try_fold_object_expression(object_expr, ctx)
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +330,97 @@ impl<'a, 'b> PeepholeRemoveDeadCode {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `{a: 1, b: 2, c: foo()}` -> `foo()`
|
||||||
|
fn try_fold_object_expression(
|
||||||
|
object_expr: &mut ObjectExpression<'a>,
|
||||||
|
ctx: Ctx<'a, 'b>,
|
||||||
|
) -> Option<Statement<'a>> {
|
||||||
|
let spread_count = object_expr
|
||||||
|
.properties
|
||||||
|
.iter()
|
||||||
|
.filter(|prop| matches!(prop, ObjectPropertyKind::SpreadProperty(_)))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if spread_count == object_expr.properties.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a spread, we can't remove the object expression
|
||||||
|
if spread_count > 0 {
|
||||||
|
let original_property_count = object_expr.properties.len();
|
||||||
|
|
||||||
|
object_expr.properties.retain(|v| match v {
|
||||||
|
ObjectPropertyKind::ObjectProperty(object_property) => {
|
||||||
|
object_property.key.may_have_side_effects()
|
||||||
|
|| object_property.value.may_have_side_effects()
|
||||||
|
|| object_property.init.as_ref().is_some_and(
|
||||||
|
oxc_ecmascript::side_effects::MayHaveSideEffects::may_have_side_effects,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ObjectPropertyKind::SpreadProperty(_) => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if original_property_count == object_expr.properties.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some(ctx.ast.statement_expression(
|
||||||
|
object_expr.span,
|
||||||
|
ctx.ast.expression_from_object(ctx.ast.object_expression(
|
||||||
|
object_expr.span,
|
||||||
|
ctx.ast.move_vec(&mut object_expr.properties),
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can replace the object with a sequence expression
|
||||||
|
let mut filtered_properties = ctx.ast.vec();
|
||||||
|
|
||||||
|
for prop in object_expr.properties.iter_mut() {
|
||||||
|
match prop {
|
||||||
|
ObjectPropertyKind::ObjectProperty(object_prop) => {
|
||||||
|
let key = object_prop.key.as_expression_mut();
|
||||||
|
if let Some(key) = key {
|
||||||
|
if key.may_have_side_effects() {
|
||||||
|
let key_expr = ctx.ast.move_expression(key);
|
||||||
|
filtered_properties.push(key_expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if object_prop.value.may_have_side_effects() {
|
||||||
|
let mut expr = ctx.ast.move_expression(&mut object_prop.value);
|
||||||
|
filtered_properties.push(ctx.ast.move_expression(&mut expr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if object_prop.init.as_ref().is_some_and(
|
||||||
|
oxc_ecmascript::side_effects::MayHaveSideEffects::may_have_side_effects,
|
||||||
|
) {
|
||||||
|
let mut expr = object_prop.init.take().unwrap();
|
||||||
|
filtered_properties.push(ctx.ast.move_expression(&mut expr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectPropertyKind::SpreadProperty(_) => {
|
||||||
|
unreachable!("spread property should have been filtered out");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if filtered_properties.len() == 0 {
|
||||||
|
return Some(ctx.ast.statement_empty(object_expr.span));
|
||||||
|
} else if filtered_properties.len() == 1 {
|
||||||
|
return Some(
|
||||||
|
ctx.ast.statement_expression(object_expr.span, filtered_properties.pop().unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(ctx.ast.statement_expression(
|
||||||
|
object_expr.span,
|
||||||
|
ctx.ast.expression_from_sequence(
|
||||||
|
ctx.ast.sequence_expression(object_expr.span, filtered_properties),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/// Try folding conditional expression (?:) if the condition results of the condition is known.
|
/// Try folding conditional expression (?:) if the condition results of the condition is known.
|
||||||
fn try_fold_conditional_expression(
|
fn try_fold_conditional_expression(
|
||||||
expr: &mut ConditionalExpression<'a>,
|
expr: &mut ConditionalExpression<'a>,
|
||||||
|
|
@ -460,11 +553,14 @@ mod test {
|
||||||
fn test_object_literal() {
|
fn test_object_literal() {
|
||||||
fold("({})", "");
|
fold("({})", "");
|
||||||
fold("({a:1})", "");
|
fold("({a:1})", "");
|
||||||
// fold("({a:foo()})", "foo()");
|
fold("({a:foo()})", "foo()");
|
||||||
// fold("({'a':foo()})", "foo()");
|
fold("({'a':foo()})", "foo()");
|
||||||
// Object-spread may trigger getters.
|
// Object-spread may trigger getters.
|
||||||
fold_same("({...a})");
|
fold_same("({...a})");
|
||||||
fold_same("({...foo()})");
|
fold_same("({...foo()})");
|
||||||
|
|
||||||
|
fold("({ [bar()]: foo() })", "bar(), foo()");
|
||||||
|
fold_same("({ ...baz, [bar()]: foo() })");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue