mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
parent
ee211baabc
commit
e090b560be
6 changed files with 163 additions and 13 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::hir::{Expression, MemberExpression};
|
||||
use crate::hir::{Declaration, Expression, MemberExpression, ModuleDeclaration, Statement};
|
||||
|
||||
impl<'a> GetSpan for Expression<'a> {
|
||||
fn span(&self) -> Span {
|
||||
|
|
@ -52,3 +52,51 @@ impl<'a> GetSpan for MemberExpression<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GetSpan for Statement<'a> {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::BlockStatement(stmt) => stmt.span,
|
||||
Self::BreakStatement(stmt) => stmt.span,
|
||||
Self::ContinueStatement(stmt) => stmt.span,
|
||||
Self::DebuggerStatement(stmt) => stmt.span,
|
||||
Self::Declaration(stmt) => stmt.span(),
|
||||
Self::DoWhileStatement(stmt) => stmt.span,
|
||||
Self::ExpressionStatement(stmt) => stmt.span,
|
||||
Self::ForInStatement(stmt) => stmt.span,
|
||||
Self::ForOfStatement(stmt) => stmt.span,
|
||||
Self::ForStatement(stmt) => stmt.span,
|
||||
Self::IfStatement(stmt) => stmt.span,
|
||||
Self::LabeledStatement(stmt) => stmt.span,
|
||||
Self::ModuleDeclaration(decl) => decl.span(),
|
||||
Self::ReturnStatement(stmt) => stmt.span,
|
||||
Self::SwitchStatement(stmt) => stmt.span,
|
||||
Self::ThrowStatement(stmt) => stmt.span,
|
||||
Self::TryStatement(stmt) => stmt.span,
|
||||
Self::WhileStatement(stmt) => stmt.span,
|
||||
Self::WithStatement(stmt) => stmt.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GetSpan for Declaration<'a> {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::ClassDeclaration(decl) => decl.span,
|
||||
Self::FunctionDeclaration(decl) => decl.span,
|
||||
Self::TSEnumDeclaration(decl) => decl.span,
|
||||
Self::VariableDeclaration(decl) => decl.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GetSpan for ModuleDeclaration<'a> {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::ExportAllDeclaration(decl) => decl.span,
|
||||
Self::ExportDefaultDeclaration(decl) => decl.span,
|
||||
Self::ExportNamedDeclaration(decl) => decl.span,
|
||||
Self::ImportDeclaration(decl) => decl.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -783,29 +783,23 @@ impl<'a> Compressor<'a> {
|
|||
) -> Option<Expression<'a>> {
|
||||
let boolean_value = get_boolean_value(&logic_expr.left);
|
||||
|
||||
let mut move_out = |dest: &mut Expression<'a>| {
|
||||
let null_literal = self.hir.null_literal(dest.span());
|
||||
let null_expr = self.hir.literal_null_expression(null_literal);
|
||||
mem::replace(dest, null_expr)
|
||||
};
|
||||
|
||||
if let Some(boolean_value) = boolean_value {
|
||||
// (TRUE || x) => TRUE (also, (3 || x) => 3)
|
||||
// (FALSE && x) => FALSE
|
||||
if (boolean_value && op == LogicalOperator::Or)
|
||||
|| (!boolean_value && op == LogicalOperator::And)
|
||||
{
|
||||
return Some(move_out(&mut logic_expr.left));
|
||||
return Some(self.move_out_expression(&mut logic_expr.left));
|
||||
} else if !logic_expr.left.may_have_side_effects() {
|
||||
// (FALSE || x) => x
|
||||
// (TRUE && x) => x
|
||||
return Some(move_out(&mut logic_expr.right));
|
||||
return Some(self.move_out_expression(&mut logic_expr.right));
|
||||
}
|
||||
// Left side may have side effects, but we know its boolean value.
|
||||
// e.g. true_with_sideeffects || foo() => true_with_sideeffects, foo()
|
||||
// or: false_with_sideeffects && foo() => false_with_sideeffects, foo()
|
||||
let left = move_out(&mut logic_expr.left);
|
||||
let right = move_out(&mut logic_expr.right);
|
||||
let left = self.move_out_expression(&mut logic_expr.left);
|
||||
let right = self.move_out_expression(&mut logic_expr.right);
|
||||
let mut vec = self.hir.new_vec_with_capacity(2);
|
||||
vec.push(left);
|
||||
vec.push(right);
|
||||
|
|
@ -822,8 +816,8 @@ impl<'a> Compressor<'a> {
|
|||
if !right_boolean && left_child_op == LogicalOperator::Or
|
||||
|| right_boolean && left_child_op == LogicalOperator::And
|
||||
{
|
||||
let left = move_out(&mut left_child.left);
|
||||
let right = move_out(&mut logic_expr.right);
|
||||
let left = self.move_out_expression(&mut left_child.left);
|
||||
let right = self.move_out_expression(&mut logic_expr.right);
|
||||
let logic_expr = self.hir.logical_expression(
|
||||
logic_expr.span,
|
||||
left,
|
||||
|
|
@ -838,4 +832,79 @@ impl<'a> Compressor<'a> {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn fold_condition<'b>(&mut self, stmt: &'b mut Statement<'a>) {
|
||||
match stmt {
|
||||
Statement::WhileStatement(while_stmt) => {
|
||||
let minimized_expr = self.fold_expression_in_condition(&mut while_stmt.0.test);
|
||||
|
||||
if let Some(min_expr) = minimized_expr {
|
||||
while_stmt.0.test = min_expr;
|
||||
}
|
||||
}
|
||||
Statement::ForStatement(for_stmt) => {
|
||||
let test_expr = for_stmt.0.test.as_mut();
|
||||
|
||||
if let Some(test_expr) = test_expr {
|
||||
let minimized_expr = self.fold_expression_in_condition(test_expr);
|
||||
|
||||
if let Some(min_expr) = minimized_expr {
|
||||
for_stmt.0.test = Some(min_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
fn fold_expression_in_condition(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let folded_expr = match expr {
|
||||
Expression::UnaryExpression(unary_expr) => match unary_expr.operator {
|
||||
UnaryOperator::LogicalNot => {
|
||||
let should_fold = self.try_minimize_not(&mut unary_expr.0.argument);
|
||||
|
||||
if should_fold {
|
||||
Some(self.move_out_expression(&mut unary_expr.0.argument))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
folded_expr
|
||||
}
|
||||
|
||||
fn move_out_expression(&mut self, expr: &mut Expression<'a>) -> Expression<'a> {
|
||||
let null_literal = self.hir.null_literal(expr.span());
|
||||
let null_expr = self.hir.literal_null_expression(null_literal);
|
||||
mem::replace(expr, null_expr)
|
||||
}
|
||||
|
||||
/// ported from [closure compiler](https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java#L401-L435)
|
||||
fn try_minimize_not(&mut self, expr: &mut Expression<'a>) -> bool {
|
||||
let span = &mut expr.span();
|
||||
|
||||
match expr {
|
||||
Expression::BinaryExpression(binary_expr) => {
|
||||
let new_op = binary_expr.0.operator.equality_inverse_operator();
|
||||
|
||||
match new_op {
|
||||
Some(new_op) => {
|
||||
binary_expr.0.operator = new_op;
|
||||
binary_expr.0.span = *span;
|
||||
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -323,6 +323,7 @@ impl<'a, 'b> VisitMut<'a, 'b> for Compressor<'a> {
|
|||
fn visit_statement(&mut self, stmt: &'b mut Statement<'a>) {
|
||||
self.compress_block(stmt);
|
||||
self.compress_while(stmt);
|
||||
self.fold_condition(stmt);
|
||||
self.visit_statement_match(stmt);
|
||||
}
|
||||
|
||||
|
|
|
|||
21
crates/oxc_minifier/tests/closure/fold_conditions.rs
Normal file
21
crates/oxc_minifier/tests/closure/fold_conditions.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use crate::test;
|
||||
|
||||
#[test]
|
||||
fn test_fold_not() {
|
||||
test("while(!(x==y)){a=b;}", "for(;x!=y;)a=b");
|
||||
test("while(!(x!=y)){a=b;}", "for(;x==y;)a=b");
|
||||
test("while(!(x===y)){a=b;}", "for(;x!==y;)a=b");
|
||||
test("while(!(x!==y)){a=b;}", "for(;x===y;)a=b");
|
||||
|
||||
// Because !(x<NaN) != x>=NaN don't fold < and > cases.
|
||||
test("while(!(x>y)){a=b;}", "for(;!(x>y);)a=b");
|
||||
test("while(!(x>=y)){a=b;}", "for(;!(x>=y);)a=b");
|
||||
test("while(!(x<y)){a=b;}", "for(;!(x<y);)a=b");
|
||||
test("while(!(x<=y)){a=b;}", "for(;!(x<=y);)a=b");
|
||||
test("while(!(x<=NaN)){a=b;}", "for(;!(x<=NaN);)a=b");
|
||||
|
||||
// NOT forces a boolean context
|
||||
// test("x = !(y() && true)", "x=!y()");
|
||||
// This will be further optimized by PeepholeFoldConstants.
|
||||
// test("x = !true", "x=!1");
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
mod fold_conditions;
|
||||
mod fold_constants;
|
||||
mod printer;
|
||||
mod reorder_constant_expression;
|
||||
|
|
|
|||
|
|
@ -199,6 +199,16 @@ impl BinaryOperator {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn equality_inverse_operator(self) -> Option<Self> {
|
||||
match self {
|
||||
Self::Equality => Some(Self::Inequality),
|
||||
Self::Inequality => Some(Self::Equality),
|
||||
Self::StrictEquality => Some(Self::StrictInequality),
|
||||
Self::StrictInequality => Some(Self::StrictEquality),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Equality => "==",
|
||||
|
|
|
|||
Loading…
Reference in a new issue