mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
feat(minifier): complete implementation of statement fusion (#6566)
- enables all test in statement fusion - implements some more node in `may_have_side_effects`
This commit is contained in:
parent
c67acfaae7
commit
ef237cf8ff
3 changed files with 178 additions and 4 deletions
|
|
@ -3,7 +3,7 @@ use oxc_ast::ast::*;
|
|||
use oxc_span::SPAN;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::CompressorPass;
|
||||
use crate::{node_util::MayHaveSideEffects, CompressorPass};
|
||||
|
||||
/// Statement Fusion
|
||||
///
|
||||
|
|
@ -73,8 +73,7 @@ impl<'a> StatementFusion {
|
|||
for_stmt.init.is_none()
|
||||
|| for_stmt.init.as_ref().is_some_and(ForStatementInit::is_expression)
|
||||
}
|
||||
// TODO: support for-in, we need to check the init for side effects
|
||||
Statement::ForInStatement(_for_in_stmt) => false,
|
||||
Statement::ForInStatement(for_in_stmt) => !for_in_stmt.left.may_have_side_effects(),
|
||||
Statement::LabeledStatement(labeled_stmt) => {
|
||||
Self::is_fusable_control_statement(&labeled_stmt.body)
|
||||
}
|
||||
|
|
@ -144,6 +143,7 @@ impl<'a> StatementFusion {
|
|||
return;
|
||||
}
|
||||
}
|
||||
Statement::ForInStatement(for_stmt) => &mut for_stmt.right,
|
||||
Statement::LabeledStatement(labeled_stmt) => {
|
||||
Self::fuse_expression_into_control_flow_statement(
|
||||
&mut labeled_stmt.body,
|
||||
|
|
@ -261,7 +261,6 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn fuse_into_for_in1() {
|
||||
fuse("a;b;c;for(x in y){}", "for(x in a,b,c,y){}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,3 +145,177 @@ impl<'a, 'b> CheckForStateChange<'a, 'b> for PropertyKey<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> CheckForStateChange<'a, 'b> for ForStatementLeft<'a> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
match self {
|
||||
match_assignment_target!(Self) => {
|
||||
self.to_assignment_target().check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
ForStatementLeft::VariableDeclaration(variable_declaration) => {
|
||||
variable_declaration.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> CheckForStateChange<'a, 'b> for VariableDeclaration<'a> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
self.declarations.iter().any(|decl| decl.check_for_state_change(check_for_new_objects))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> CheckForStateChange<'a, 'b> for VariableDeclarator<'a> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
self.id.check_for_state_change(check_for_new_objects)
|
||||
|| self
|
||||
.init
|
||||
.as_ref()
|
||||
.map_or(false, |init| init.check_for_state_change(check_for_new_objects))
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckForStateChange<'_, '_> for BindingPattern<'_> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
match &self.kind {
|
||||
BindingPatternKind::BindingIdentifier(_) => false,
|
||||
BindingPatternKind::ObjectPattern(object_pattern) => {
|
||||
object_pattern
|
||||
.properties
|
||||
.iter()
|
||||
.any(|element| element.check_for_state_change(check_for_new_objects))
|
||||
|| object_pattern
|
||||
.rest
|
||||
.as_ref()
|
||||
.is_some_and(|rest| rest.check_for_state_change(check_for_new_objects))
|
||||
}
|
||||
BindingPatternKind::ArrayPattern(array_pattern) => {
|
||||
array_pattern.elements.iter().any(|element| {
|
||||
element.as_ref().is_some_and(|element| {
|
||||
element.check_for_state_change(check_for_new_objects)
|
||||
})
|
||||
}) || array_pattern
|
||||
.rest
|
||||
.as_ref()
|
||||
.is_some_and(|rest| rest.check_for_state_change(check_for_new_objects))
|
||||
}
|
||||
BindingPatternKind::AssignmentPattern(assignment_pattern) => {
|
||||
assignment_pattern.left.check_for_state_change(check_for_new_objects)
|
||||
&& assignment_pattern.right.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckForStateChange<'_, '_> for BindingRestElement<'_> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
self.argument.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckForStateChange<'_, '_> for BindingProperty<'_> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
self.key.check_for_state_change(check_for_new_objects)
|
||||
|| self.value.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckForStateChange<'_, '_> for AssignmentTarget<'_> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
match self {
|
||||
AssignmentTarget::AssignmentTargetIdentifier(_) => false,
|
||||
AssignmentTarget::TSAsExpression(ts_as_expression) => {
|
||||
ts_as_expression.expression.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::TSSatisfiesExpression(ts_satisfies_expression) => {
|
||||
ts_satisfies_expression.expression.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::TSNonNullExpression(ts_non_null_expression) => {
|
||||
ts_non_null_expression.expression.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::TSTypeAssertion(ts_type_assertion) => {
|
||||
ts_type_assertion.expression.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::TSInstantiationExpression(ts_instantiation_expression) => {
|
||||
ts_instantiation_expression.expression.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::ComputedMemberExpression(computed_member_expression) => {
|
||||
computed_member_expression.object.check_for_state_change(check_for_new_objects)
|
||||
|| computed_member_expression
|
||||
.expression
|
||||
.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::StaticMemberExpression(static_member_expression) => {
|
||||
static_member_expression.object.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::PrivateFieldExpression(private_field_expression) => {
|
||||
private_field_expression.object.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
AssignmentTarget::ArrayAssignmentTarget(array_assignment_target) => {
|
||||
array_assignment_target.elements.iter().any(|element| {
|
||||
element.as_ref().is_some_and(|element| {
|
||||
element.check_for_state_change(check_for_new_objects)
|
||||
})
|
||||
}) || array_assignment_target
|
||||
.rest
|
||||
.as_ref()
|
||||
.is_some_and(|rest| rest.check_for_state_change(check_for_new_objects))
|
||||
}
|
||||
AssignmentTarget::ObjectAssignmentTarget(object_assignment_target) => {
|
||||
object_assignment_target
|
||||
.properties
|
||||
.iter()
|
||||
.any(|property| property.check_for_state_change(check_for_new_objects))
|
||||
|| object_assignment_target
|
||||
.rest
|
||||
.as_ref()
|
||||
.is_some_and(|rest| rest.check_for_state_change(check_for_new_objects))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckForStateChange<'_, '_> for AssignmentTargetProperty<'_> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
match self {
|
||||
AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(
|
||||
assignment_target_property_identifier,
|
||||
) => assignment_target_property_identifier
|
||||
.init
|
||||
.as_ref()
|
||||
.map_or(false, |init| init.check_for_state_change(check_for_new_objects)),
|
||||
AssignmentTargetProperty::AssignmentTargetPropertyProperty(
|
||||
assignment_target_property_property,
|
||||
) => {
|
||||
assignment_target_property_property
|
||||
.name
|
||||
.check_for_state_change(check_for_new_objects)
|
||||
|| assignment_target_property_property
|
||||
.binding
|
||||
.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckForStateChange<'_, '_> for AssignmentTargetRest<'_> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
self.target.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckForStateChange<'_, '_> for AssignmentTargetMaybeDefault<'_> {
|
||||
fn check_for_state_change(&self, check_for_new_objects: bool) -> bool {
|
||||
match self {
|
||||
match_assignment_target!(Self) => {
|
||||
self.to_assignment_target().check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
Self::AssignmentTargetWithDefault(assignment_target_with_default) => {
|
||||
assignment_target_with_default.binding.check_for_state_change(check_for_new_objects)
|
||||
&& assignment_target_with_default
|
||||
.init
|
||||
.check_for_state_change(check_for_new_objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,3 +18,4 @@ where
|
|||
|
||||
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 ForStatementLeft<'a> {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue