feat(minifier): initialize constant folding (#393)

Co-authored-by: Wenzhe Wang <mysteryven@gmail.com>
This commit is contained in:
Boshen 2023-05-28 11:32:10 +08:00 committed by GitHub
parent 32257dce82
commit 6ffc348e34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 147 additions and 1 deletions

View file

@ -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
}
}

View file

@ -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)

View file

@ -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");
}