mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(minifier): initialize constant folding (#393)
Co-authored-by: Wenzhe Wang <mysteryven@gmail.com>
This commit is contained in:
parent
32257dce82
commit
6ffc348e34
3 changed files with 147 additions and 1 deletions
|
|
@ -0,0 +1,139 @@
|
|||
//! Constant Folding
|
||||
//!
|
||||
//! <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
|
||||
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_hir::hir::*;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::operator::BinaryOperator;
|
||||
|
||||
use super::Compressor;
|
||||
|
||||
/// Tri state
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Tri {
|
||||
True,
|
||||
False,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// JavaScript Language Type
|
||||
///
|
||||
/// <https://tc39.es/ecma262/#sec-ecmascript-language-types>
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Ty {
|
||||
BigInt,
|
||||
Boolean,
|
||||
Null,
|
||||
Number,
|
||||
Object,
|
||||
Str,
|
||||
Void,
|
||||
Undetermined,
|
||||
}
|
||||
|
||||
impl<'a> From<&Expression<'a>> for Ty {
|
||||
fn from(expr: &Expression<'a>) -> Self {
|
||||
// TODO: complete this
|
||||
match expr {
|
||||
Expression::BigintLiteral(_) => Self::BigInt,
|
||||
Expression::BooleanLiteral(_) => Self::Boolean,
|
||||
Expression::NullLiteral(_) => Self::Null,
|
||||
Expression::NumberLiteral(_) => Self::Number,
|
||||
Expression::ObjectExpression(_) => Self::Object,
|
||||
Expression::StringLiteral(_) => Self::Str,
|
||||
Expression::Identifier(ident) => match ident.name.as_str() {
|
||||
"undefined" => Self::Void,
|
||||
_ => Self::Undetermined,
|
||||
},
|
||||
_ => Self::Undetermined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Compressor<'a> {
|
||||
pub(crate) fn fold_expression<'b>(&mut self, expr: &'b mut Expression<'a>) {
|
||||
let folded_expr = match expr {
|
||||
Expression::BinaryExpression(binary_expr) => match binary_expr.operator {
|
||||
BinaryOperator::Equality => self.try_fold_comparison(
|
||||
binary_expr.span,
|
||||
binary_expr.operator,
|
||||
&binary_expr.left,
|
||||
&binary_expr.right,
|
||||
),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let Some(folded_expr) = folded_expr {
|
||||
*expr = folded_expr;
|
||||
}
|
||||
}
|
||||
|
||||
fn try_fold_comparison<'b>(
|
||||
&mut self,
|
||||
span: Span,
|
||||
op: BinaryOperator,
|
||||
left: &'b Expression<'a>,
|
||||
right: &'b Expression<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let value = match self.evaluate_comparison(op, left, right) {
|
||||
Tri::True => true,
|
||||
Tri::False => false,
|
||||
Tri::Unknown => return None,
|
||||
};
|
||||
let boolean_literal = self.hir.boolean_literal(span, value);
|
||||
Some(self.hir.literal_boolean_expression(boolean_literal))
|
||||
}
|
||||
|
||||
fn evaluate_comparison<'b>(
|
||||
&self,
|
||||
op: BinaryOperator,
|
||||
left: &'b Expression<'a>,
|
||||
right: &'b Expression<'a>,
|
||||
) -> Tri {
|
||||
match op {
|
||||
BinaryOperator::Equality => self.try_abstract_equality_comparison(left, right),
|
||||
_ => Tri::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
|
||||
fn try_abstract_equality_comparison<'b>(
|
||||
&self,
|
||||
left_expr: &'b Expression<'a>,
|
||||
right_expr: &'b Expression<'a>,
|
||||
) -> Tri {
|
||||
let left = Ty::from(left_expr);
|
||||
let right = Ty::from(right_expr);
|
||||
if left != Ty::Undetermined && right != Ty::Undetermined {
|
||||
if left == right {
|
||||
return self.try_strict_equality_comparison(left_expr, right_expr);
|
||||
}
|
||||
if matches!((left, right), (Ty::Null, Ty::Void) | (Ty::Void, Ty::Null)) {
|
||||
return Tri::True;
|
||||
}
|
||||
}
|
||||
Tri::Unknown
|
||||
}
|
||||
|
||||
/// <https://tc39.es/ecma262/#sec-strict-equality-comparison>
|
||||
fn try_strict_equality_comparison<'b>(
|
||||
&self,
|
||||
left_expr: &'b Expression<'a>,
|
||||
right_expr: &'b Expression<'a>,
|
||||
) -> Tri {
|
||||
let left = Ty::from(left_expr);
|
||||
let right = Ty::from(right_expr);
|
||||
if left != Ty::Undetermined && right != Ty::Undetermined {
|
||||
if left != right {
|
||||
return Tri::False;
|
||||
}
|
||||
return match left {
|
||||
Ty::Void | Ty::Null => Tri::True,
|
||||
_ => Tri::Unknown,
|
||||
};
|
||||
}
|
||||
Tri::Unknown
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
#![allow(clippy::unused_self)]
|
||||
|
||||
mod fold;
|
||||
|
||||
use oxc_allocator::{Allocator, Vec};
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_hir::{hir::*, HirBuilder, VisitMut};
|
||||
|
|
@ -275,6 +277,7 @@ impl<'a, 'b> VisitMut<'a, 'b> for Compressor<'a> {
|
|||
}
|
||||
|
||||
fn visit_expression(&mut self, expr: &'b mut Expression<'a>) {
|
||||
self.fold_expression(expr);
|
||||
if self.compress_undefined(expr)
|
||||
|| self.compress_boolean(expr)
|
||||
|| self.compress_infinity(expr)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
//! <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java>
|
||||
|
||||
use crate::test;
|
||||
|
||||
#[test]
|
||||
fn undefined_comparison1() {}
|
||||
fn undefined_comparison1() {
|
||||
test("undefined == undefined", "!0");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue