mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(mininifier): minimize variants of a instanceof b == true (#8241)
This commit is contained in:
parent
ccdc039f54
commit
ad9a0a9c4a
3 changed files with 116 additions and 59 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use oxc_ast::ast::Expression;
|
use oxc_ast::ast::{BinaryExpression, Expression};
|
||||||
use oxc_syntax::operator::{BinaryOperator, UnaryOperator};
|
use oxc_syntax::operator::{BinaryOperator, UnaryOperator};
|
||||||
|
|
||||||
/// JavaScript Language Type
|
/// JavaScript Language Type
|
||||||
|
|
@ -81,14 +81,25 @@ impl<'a> From<&Expression<'a>> for ValueType {
|
||||||
Self::Number
|
Self::Number
|
||||||
}
|
}
|
||||||
UnaryOperator::UnaryPlus => Self::Number,
|
UnaryOperator::UnaryPlus => Self::Number,
|
||||||
UnaryOperator::LogicalNot => Self::Boolean,
|
UnaryOperator::LogicalNot | UnaryOperator::Delete => Self::Boolean,
|
||||||
UnaryOperator::Typeof => Self::String,
|
UnaryOperator::Typeof => Self::String,
|
||||||
_ => Self::Undetermined,
|
UnaryOperator::BitwiseNot => Self::Undetermined,
|
||||||
},
|
},
|
||||||
Expression::BinaryExpression(binary_expr) => match binary_expr.operator {
|
Expression::BinaryExpression(e) => Self::from(&**e),
|
||||||
|
Expression::SequenceExpression(e) => {
|
||||||
|
e.expressions.last().map_or(ValueType::Undetermined, Self::from)
|
||||||
|
}
|
||||||
|
_ => Self::Undetermined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&BinaryExpression<'a>> for ValueType {
|
||||||
|
fn from(e: &BinaryExpression<'a>) -> Self {
|
||||||
|
match e.operator {
|
||||||
BinaryOperator::Addition => {
|
BinaryOperator::Addition => {
|
||||||
let left_ty = Self::from(&binary_expr.left);
|
let left_ty = Self::from(&e.left);
|
||||||
let right_ty = Self::from(&binary_expr.right);
|
let right_ty = Self::from(&e.right);
|
||||||
if left_ty == Self::String || right_ty == Self::String {
|
if left_ty == Self::String || right_ty == Self::String {
|
||||||
return Self::String;
|
return Self::String;
|
||||||
}
|
}
|
||||||
|
|
@ -100,11 +111,7 @@ impl<'a> From<&Expression<'a>> for ValueType {
|
||||||
}
|
}
|
||||||
Self::Undetermined
|
Self::Undetermined
|
||||||
}
|
}
|
||||||
_ => Self::Undetermined,
|
BinaryOperator::Instanceof => Self::Boolean,
|
||||||
},
|
|
||||||
Expression::SequenceExpression(e) => {
|
|
||||||
e.expressions.last().map_or(ValueType::Undetermined, Self::from)
|
|
||||||
}
|
|
||||||
_ => Self::Undetermined,
|
_ => Self::Undetermined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use oxc_allocator::Vec;
|
use oxc_allocator::Vec;
|
||||||
use oxc_ast::ast::*;
|
use oxc_ast::ast::*;
|
||||||
|
use oxc_ecmascript::constant_evaluation::ValueType;
|
||||||
use oxc_span::{GetSpan, SPAN};
|
use oxc_span::{GetSpan, SPAN};
|
||||||
use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx};
|
use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx};
|
||||||
|
|
||||||
|
|
@ -57,12 +58,9 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions {
|
||||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
if let Some(folded_expr) = match expr {
|
if let Some(folded_expr) = match expr {
|
||||||
Expression::UnaryExpression(e) => Self::try_minimize_not(e, ctx),
|
Expression::UnaryExpression(e) => Self::try_minimize_not(e, ctx),
|
||||||
Expression::LogicalExpression(logical_expr) => {
|
Expression::LogicalExpression(e) => Self::try_minimize_logical(e, ctx),
|
||||||
Self::try_minimize_logical(logical_expr, ctx)
|
Expression::BinaryExpression(e) => Self::try_minimize_binary(e, ctx),
|
||||||
}
|
Expression::ConditionalExpression(e) => Self::try_minimize_conditional(e, ctx),
|
||||||
Expression::ConditionalExpression(conditional_expr) => {
|
|
||||||
Self::try_minimize_conditional(conditional_expr, ctx)
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
*expr = folded_expr;
|
*expr = folded_expr;
|
||||||
|
|
@ -275,7 +273,7 @@ impl<'a> PeepholeMinimizeConditions {
|
||||||
Expression::BooleanLiteral(consequent_lit),
|
Expression::BooleanLiteral(consequent_lit),
|
||||||
) = (&expr.left, &expr.right)
|
) = (&expr.left, &expr.right)
|
||||||
{
|
{
|
||||||
if !is_in_boolean_context(ctx) {
|
if !Self::is_in_boolean_context(ctx) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if consequent_lit.value {
|
if consequent_lit.value {
|
||||||
|
|
@ -357,7 +355,7 @@ impl<'a> PeepholeMinimizeConditions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let in_boolean_context = is_in_boolean_context(ctx);
|
let in_boolean_context = Self::is_in_boolean_context(ctx);
|
||||||
|
|
||||||
// `a ? false : true` -> `!a`
|
// `a ? false : true` -> `!a`
|
||||||
// `a ? true : false` -> `!!a`
|
// `a ? true : false` -> `!!a`
|
||||||
|
|
@ -456,7 +454,6 @@ impl<'a> PeepholeMinimizeConditions {
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// returns `true` if the current node is in a context in which the return
|
// returns `true` if the current node is in a context in which the return
|
||||||
// value type is coerced to boolean.
|
// value type is coerced to boolean.
|
||||||
|
|
@ -491,6 +488,36 @@ fn is_in_boolean_context(ctx: &mut TraverseCtx<'_>) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_minimize_binary(
|
||||||
|
e: &mut BinaryExpression<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) -> Option<Expression<'a>> {
|
||||||
|
// let ctx = Ctx(ctx);
|
||||||
|
if !ValueType::from(&e.left).is_boolean() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let Expression::BooleanLiteral(b) = &mut e.right else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
match e.operator {
|
||||||
|
BinaryOperator::Inequality | BinaryOperator::StrictInequality => {
|
||||||
|
e.operator = BinaryOperator::Equality;
|
||||||
|
b.value = !b.value;
|
||||||
|
}
|
||||||
|
BinaryOperator::StrictEquality => {
|
||||||
|
e.operator = BinaryOperator::Equality;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Some(if b.value {
|
||||||
|
ctx.ast.move_expression(&mut e.left)
|
||||||
|
} else {
|
||||||
|
let argument = ctx.ast.move_expression(&mut e.left);
|
||||||
|
ctx.ast.expression_unary(e.span, UnaryOperator::LogicalNot, argument)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/PeepholeMinimizeConditionsTest.java>
|
/// <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/PeepholeMinimizeConditionsTest.java>
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
@ -1598,4 +1625,27 @@ mod test {
|
||||||
test_same("x.y ? x.y : bar");
|
test_same("x.y ? x.y : bar");
|
||||||
test_same("x.y ? bar : x.y");
|
test_same("x.y ? bar : x.y");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compress_binary() {
|
||||||
|
test("a instanceof b === true", "a instanceof b");
|
||||||
|
test("a instanceof b == true", "a instanceof b");
|
||||||
|
test("a instanceof b === false", "!(a instanceof b)");
|
||||||
|
test("a instanceof b == false", "!(a instanceof b)");
|
||||||
|
|
||||||
|
test("a instanceof b !== true", "!(a instanceof b)");
|
||||||
|
test("a instanceof b != true", "!(a instanceof b)");
|
||||||
|
test("a instanceof b !== false", "a instanceof b");
|
||||||
|
test("a instanceof b != false", "a instanceof b");
|
||||||
|
|
||||||
|
test("delete x === true", "delete x");
|
||||||
|
test("delete x == true", "delete x");
|
||||||
|
test("delete x === false", "!(delete x)");
|
||||||
|
test("delete x == false", "!(delete x)");
|
||||||
|
|
||||||
|
test("delete x !== true", "!(delete x)");
|
||||||
|
test("delete x != true", "!(delete x)");
|
||||||
|
test("delete x !== false", "delete x");
|
||||||
|
test("delete x != false", "delete x");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ Original | minified | minified | gzip | gzip | Fixture
|
||||||
|
|
||||||
2.14 MB | 726.19 kB | 724.14 kB | 180.18 kB | 181.07 kB | victory.js
|
2.14 MB | 726.19 kB | 724.14 kB | 180.18 kB | 181.07 kB | victory.js
|
||||||
|
|
||||||
3.20 MB | 1.01 MB | 1.01 MB | 331.91 kB | 331.56 kB | echarts.js
|
3.20 MB | 1.01 MB | 1.01 MB | 331.90 kB | 331.56 kB | echarts.js
|
||||||
|
|
||||||
6.69 MB | 2.32 MB | 2.31 MB | 492.80 kB | 488.28 kB | antd.js
|
6.69 MB | 2.32 MB | 2.31 MB | 492.80 kB | 488.28 kB | antd.js
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue