From e56188037cb1dc9b53ba1038277bf13760b9be77 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Tue, 15 Oct 2024 05:15:46 +0000 Subject: [PATCH] feat(ecmascript): add constant_evaluation and side_effects code (#6550) I intend to move most of the constant evaluation code into the crate. --- crates/oxc_ecmascript/Cargo.toml | 2 +- .../oxc_ecmascript/src/constant_evaluation.rs | 171 --------- .../constant_evaluation/is_litral_value.rs} | 1 + .../src/constant_evaluation/mod.rs | 363 ++++++++++++++++++ .../src/constant_evaluation/type.rs} | 12 +- .../src/constant_evaluation/value.rs | 73 ++++ crates/oxc_ecmascript/src/lib.rs | 23 +- .../side_effects}/check_for_state_change.rs | 1 + .../side_effects}/may_have_side_effects.rs | 2 +- crates/oxc_ecmascript/src/side_effects/mod.rs | 5 + .../src/ast_passes/peephole_fold_constants.rs | 7 +- .../ast_passes/peephole_remove_dead_code.rs | 4 +- .../src/ast_passes/statement_fusion.rs | 3 +- crates/oxc_minifier/src/lib.rs | 1 - crates/oxc_minifier/src/node_util/mod.rs | 8 +- 15 files changed, 475 insertions(+), 201 deletions(-) delete mode 100644 crates/oxc_ecmascript/src/constant_evaluation.rs rename crates/{oxc_minifier/src/node_util/is_literal_value.rs => oxc_ecmascript/src/constant_evaluation/is_litral_value.rs} (98%) create mode 100644 crates/oxc_ecmascript/src/constant_evaluation/mod.rs rename crates/{oxc_minifier/src/value_type.rs => oxc_ecmascript/src/constant_evaluation/type.rs} (94%) create mode 100644 crates/oxc_ecmascript/src/constant_evaluation/value.rs rename crates/{oxc_minifier/src/node_util => oxc_ecmascript/src/side_effects}/check_for_state_change.rs (99%) rename crates/{oxc_minifier/src/node_util => oxc_ecmascript/src/side_effects}/may_have_side_effects.rs (92%) create mode 100644 crates/oxc_ecmascript/src/side_effects/mod.rs diff --git a/crates/oxc_ecmascript/Cargo.toml b/crates/oxc_ecmascript/Cargo.toml index d4dd533a989b6..c3d294e6eafea 100644 --- a/crates/oxc_ecmascript/Cargo.toml +++ b/crates/oxc_ecmascript/Cargo.toml @@ -23,7 +23,7 @@ doctest = false [dependencies] oxc_ast = { workspace = true } oxc_span = { workspace = true } -oxc_syntax = { workspace = true } +oxc_syntax = { workspace = true, features = ["to_js_string"] } num-bigint = { workspace = true } num-traits = { workspace = true } diff --git a/crates/oxc_ecmascript/src/constant_evaluation.rs b/crates/oxc_ecmascript/src/constant_evaluation.rs deleted file mode 100644 index c23dea898d2ed..0000000000000 --- a/crates/oxc_ecmascript/src/constant_evaluation.rs +++ /dev/null @@ -1,171 +0,0 @@ -use core::f64; -use std::borrow::Cow; - -use num_traits::Zero; -#[allow(clippy::wildcard_imports)] -use oxc_ast::ast::*; - -pub enum ConstantValue<'a> { - Number(f64), - String(Cow<'a, str>), - Identifier, - Undefined, -} - -// impl<'a> ConstantValue<'a> { -// fn to_boolean(&self) -> Option { -// match self { -// Self::Number(n) => Some(!n.is_zero()), -// Self::String(s) => Some(!s.is_empty()), -// Self::Identifier => None, -// Self::Undefined => Some(false), -// } -// } -// } - -pub trait ConstantEvaluation<'a> { - fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> bool { - matches!(ident.name.as_str(), "undefined" | "NaN" | "Infinity") - } - - fn resolve_binding(&self, ident: &IdentifierReference<'a>) -> Option { - match ident.name.as_str() { - "undefined" if self.is_global_reference(ident) => Some(ConstantValue::Undefined), - "NaN" if self.is_global_reference(ident) => Some(ConstantValue::Number(f64::NAN)), - "Infinity" if self.is_global_reference(ident) => { - Some(ConstantValue::Number(f64::INFINITY)) - } - _ => None, - } - } - - fn eval_to_boolean(&self, expr: &Expression<'a>) -> Option { - match expr { - Expression::Identifier(ident) => match ident.name.as_str() { - "undefined" | "NaN" if self.is_global_reference(ident) => Some(false), - "Infinity" if self.is_global_reference(ident) => Some(true), - _ => None, - }, - Expression::LogicalExpression(logical_expr) => { - match logical_expr.operator { - // true && true -> true - // true && false -> false - // a && true -> None - LogicalOperator::And => { - let left = self.eval_to_boolean(&logical_expr.left); - let right = self.eval_to_boolean(&logical_expr.right); - match (left, right) { - (Some(true), Some(true)) => Some(true), - (Some(false), _) | (_, Some(false)) => Some(false), - (None, _) | (_, None) => None, - } - } - // true || false -> true - // false || false -> false - // a || b -> None - LogicalOperator::Or => { - let left = self.eval_to_boolean(&logical_expr.left); - let right = self.eval_to_boolean(&logical_expr.right); - match (left, right) { - (Some(true), _) | (_, Some(true)) => Some(true), - (Some(false), Some(false)) => Some(false), - (None, _) | (_, None) => None, - } - } - LogicalOperator::Coalesce => None, - } - } - Expression::SequenceExpression(sequence_expr) => { - // For sequence expression, the value is the value of the RHS. - sequence_expr.expressions.last().and_then(|e| self.eval_to_boolean(e)) - } - Expression::UnaryExpression(unary_expr) => { - match unary_expr.operator { - UnaryOperator::Void => Some(false), - - UnaryOperator::BitwiseNot - | UnaryOperator::UnaryPlus - | UnaryOperator::UnaryNegation => { - // `~0 -> true` `+1 -> true` `+0 -> false` `-0 -> false` - self.eval_to_number(expr).map(|value| !value.is_zero()) - } - UnaryOperator::LogicalNot => { - // !true -> false - self.eval_to_boolean(&unary_expr.argument).map(|b| !b) - } - _ => None, - } - } - Expression::AssignmentExpression(assign_expr) => { - match assign_expr.operator { - AssignmentOperator::LogicalAnd | AssignmentOperator::LogicalOr => None, - // For ASSIGN, the value is the value of the RHS. - _ => self.eval_to_boolean(&assign_expr.right), - } - } - expr => { - use crate::ToBoolean; - expr.to_boolean() - } - } - } - - fn eval_to_number(&self, expr: &Expression<'a>) -> Option { - match expr { - Expression::Identifier(ident) => match ident.name.as_str() { - "undefined" | "NaN" if self.is_global_reference(ident) => Some(f64::NAN), - "Infinity" if self.is_global_reference(ident) => Some(f64::INFINITY), - _ => None, - }, - Expression::UnaryExpression(unary_expr) => match unary_expr.operator { - UnaryOperator::UnaryPlus => self.eval_to_number(&unary_expr.argument), - UnaryOperator::UnaryNegation => { - self.eval_to_number(&unary_expr.argument).map(|v| -v) - } - // UnaryOperator::BitwiseNot => { - // unary_expr.argument.to_number().map(|value| { - // match value { - // NumberValue::Number(num) => NumberValue::Number(f64::from( - // !NumericLiteral::ecmascript_to_int32(num), - // )), - // // ~Infinity -> -1 - // // ~-Infinity -> -1 - // // ~NaN -> -1 - // _ => NumberValue::Number(-1_f64), - // } - // }) - // } - UnaryOperator::LogicalNot => { - self.eval_to_boolean(expr).map(|b| if b { 1_f64 } else { 0_f64 }) - } - UnaryOperator::Void => Some(f64::NAN), - _ => None, - }, - expr => { - use crate::ToNumber; - expr.to_number() - } - } - } - - fn eval_expression(&self, expr: &Expression<'a>) -> Option { - match expr { - Expression::LogicalExpression(e) => self.eval_logical_expression(e), - Expression::Identifier(ident) => self.resolve_binding(ident), - _ => None, - } - } - - fn eval_logical_expression(&self, expr: &LogicalExpression<'a>) -> Option { - match expr.operator { - LogicalOperator::And => { - if self.eval_to_boolean(&expr.left) == Some(true) { - self.eval_expression(&expr.right) - } else { - self.eval_expression(&expr.left) - } - } - _ => None, - } - } -} diff --git a/crates/oxc_minifier/src/node_util/is_literal_value.rs b/crates/oxc_ecmascript/src/constant_evaluation/is_litral_value.rs similarity index 98% rename from crates/oxc_minifier/src/node_util/is_literal_value.rs rename to crates/oxc_ecmascript/src/constant_evaluation/is_litral_value.rs index 78a830e2d62de..5c57359c85c9b 100644 --- a/crates/oxc_minifier/src/node_util/is_literal_value.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/is_litral_value.rs @@ -1,3 +1,4 @@ +#[allow(clippy::wildcard_imports)] use oxc_ast::ast::*; /// Returns true if this is a literal value. We define a literal value as any node that evaluates diff --git a/crates/oxc_ecmascript/src/constant_evaluation/mod.rs b/crates/oxc_ecmascript/src/constant_evaluation/mod.rs new file mode 100644 index 0000000000000..a9b1e2358b88e --- /dev/null +++ b/crates/oxc_ecmascript/src/constant_evaluation/mod.rs @@ -0,0 +1,363 @@ +mod is_litral_value; +mod r#type; +mod value; + +use std::borrow::Cow; + +use num_bigint::BigInt; +use num_traits::{One, Zero}; + +#[allow(clippy::wildcard_imports)] +use oxc_ast::ast::*; + +use crate::{side_effects::MayHaveSideEffects, ToInt32, ToJsString}; + +pub use self::{is_litral_value::IsLiteralValue, r#type::ValueType, value::ConstantValue}; + +pub trait ConstantEvaluation<'a> { + fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> bool { + matches!(ident.name.as_str(), "undefined" | "NaN" | "Infinity") + } + + fn resolve_binding(&self, ident: &IdentifierReference<'a>) -> Option> { + match ident.name.as_str() { + "undefined" if self.is_global_reference(ident) => Some(ConstantValue::Undefined), + "NaN" if self.is_global_reference(ident) => Some(ConstantValue::Number(f64::NAN)), + "Infinity" if self.is_global_reference(ident) => { + Some(ConstantValue::Number(f64::INFINITY)) + } + _ => None, + } + } + + fn get_side_free_number_value(&self, expr: &Expression<'a>) -> Option { + let value = self.eval_to_number(expr); + // Calculating the number value, if any, is likely to be faster than calculating side effects, + // and there are only a very few cases where we can compute a number value, but there could + // also be side effects. e.g. `void doSomething()` has value NaN, regardless of the behavior + // of `doSomething()` + if value.is_some() && expr.may_have_side_effects() { + None + } else { + value + } + } + + fn eval_to_boolean(&self, expr: &Expression<'a>) -> Option { + match expr { + Expression::Identifier(ident) => match ident.name.as_str() { + "undefined" | "NaN" if self.is_global_reference(ident) => Some(false), + "Infinity" if self.is_global_reference(ident) => Some(true), + _ => None, + }, + Expression::LogicalExpression(logical_expr) => { + match logical_expr.operator { + // true && true -> true + // true && false -> false + // a && true -> None + LogicalOperator::And => { + let left = self.eval_to_boolean(&logical_expr.left); + let right = self.eval_to_boolean(&logical_expr.right); + match (left, right) { + (Some(true), Some(true)) => Some(true), + (Some(false), _) | (_, Some(false)) => Some(false), + (None, _) | (_, None) => None, + } + } + // true || false -> true + // false || false -> false + // a || b -> None + LogicalOperator::Or => { + let left = self.eval_to_boolean(&logical_expr.left); + let right = self.eval_to_boolean(&logical_expr.right); + match (left, right) { + (Some(true), _) | (_, Some(true)) => Some(true), + (Some(false), Some(false)) => Some(false), + (None, _) | (_, None) => None, + } + } + LogicalOperator::Coalesce => None, + } + } + Expression::SequenceExpression(sequence_expr) => { + // For sequence expression, the value is the value of the RHS. + sequence_expr.expressions.last().and_then(|e| self.eval_to_boolean(e)) + } + Expression::UnaryExpression(unary_expr) => { + match unary_expr.operator { + UnaryOperator::Void => Some(false), + + UnaryOperator::BitwiseNot + | UnaryOperator::UnaryPlus + | UnaryOperator::UnaryNegation => { + // `~0 -> true` `+1 -> true` `+0 -> false` `-0 -> false` + self.eval_to_number(expr).map(|value| !value.is_zero()) + } + UnaryOperator::LogicalNot => { + // !true -> false + self.eval_to_boolean(&unary_expr.argument).map(|b| !b) + } + _ => None, + } + } + Expression::AssignmentExpression(assign_expr) => { + match assign_expr.operator { + AssignmentOperator::LogicalAnd | AssignmentOperator::LogicalOr => None, + // For ASSIGN, the value is the value of the RHS. + _ => self.eval_to_boolean(&assign_expr.right), + } + } + expr => { + use crate::ToBoolean; + expr.to_boolean() + } + } + } + + fn eval_to_number(&self, expr: &Expression<'a>) -> Option { + match expr { + Expression::Identifier(ident) => match ident.name.as_str() { + "undefined" | "NaN" if self.is_global_reference(ident) => Some(f64::NAN), + "Infinity" if self.is_global_reference(ident) => Some(f64::INFINITY), + _ => None, + }, + Expression::UnaryExpression(unary_expr) => match unary_expr.operator { + UnaryOperator::UnaryPlus => self.eval_to_number(&unary_expr.argument), + UnaryOperator::UnaryNegation => { + self.eval_to_number(&unary_expr.argument).map(|v| -v) + } + UnaryOperator::LogicalNot => { + self.eval_to_boolean(expr).map(|b| if b { 1_f64 } else { 0_f64 }) + } + UnaryOperator::Void => Some(f64::NAN), + _ => None, + }, + expr => { + use crate::ToNumber; + expr.to_number() + } + } + } + + fn eval_to_big_int(&self, expr: &Expression<'a>) -> Option { + match expr { + Expression::UnaryExpression(unary_expr) => match unary_expr.operator { + UnaryOperator::UnaryPlus => self.eval_to_big_int(&unary_expr.argument), + UnaryOperator::UnaryNegation => { + self.eval_to_big_int(&unary_expr.argument).map(|v| -v) + } + _ => None, + }, + Expression::BigIntLiteral(_) => { + use crate::ToBigInt; + expr.to_big_int() + } + _ => None, + } + } + + fn eval_expression(&self, expr: &Expression<'a>) -> Option> { + match expr { + Expression::BinaryExpression(e) => self.eval_binary_expression(e), + Expression::LogicalExpression(e) => self.eval_logical_expression(e), + Expression::UnaryExpression(e) => self.eval_unary_expression(e), + Expression::Identifier(ident) => self.resolve_binding(ident), + Expression::NumericLiteral(lit) => Some(ConstantValue::Number(lit.value)), + Expression::StringLiteral(lit) => { + Some(ConstantValue::String(Cow::Borrowed(lit.value.as_str()))) + } + _ => None, + } + } + + fn eval_binary_expression(&self, expr: &BinaryExpression<'a>) -> Option> { + match expr.operator { + BinaryOperator::Addition => { + let left = &expr.left; + let right = &expr.right; + if left.may_have_side_effects() || right.may_have_side_effects() { + return None; + } + let left_type = ValueType::from(left); + let right_type = ValueType::from(right); + if left_type.is_string() || right_type.is_string() { + let lval = self.eval_expression(&expr.left)?; + let rval = self.eval_expression(&expr.right)?; + let lstr = lval.to_js_string()?; + let rstr = rval.to_js_string()?; + return Some(ConstantValue::String(lstr + rstr)); + } + if left_type.is_number() || right_type.is_number() { + let lval = self.eval_expression(&expr.left)?; + let rval = self.eval_expression(&expr.right)?; + let lnum = lval.into_number()?; + let rnum = rval.into_number()?; + return Some(ConstantValue::Number(lnum + rnum)); + } + None + } + BinaryOperator::Subtraction + | BinaryOperator::Division + | BinaryOperator::Remainder + | BinaryOperator::Multiplication + | BinaryOperator::Exponential => { + let lval = self.eval_to_number(&expr.left)?; + let rval = self.eval_to_number(&expr.right)?; + let val = match expr.operator { + BinaryOperator::Subtraction => lval - rval, + BinaryOperator::Division => { + if rval.is_zero() { + if lval.is_sign_positive() { + f64::INFINITY + } else { + f64::NEG_INFINITY + } + } else { + lval / rval + } + } + BinaryOperator::Remainder => { + if !rval.is_zero() && rval.is_finite() { + lval % rval + } else if rval.is_infinite() { + f64::NAN + } else { + return None; + } + } + BinaryOperator::Multiplication => lval * rval, + BinaryOperator::Exponential => lval.powf(rval), + _ => unreachable!(), + }; + Some(ConstantValue::Number(val)) + } + #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)] + BinaryOperator::ShiftLeft + | BinaryOperator::ShiftRight + | BinaryOperator::ShiftRightZeroFill => { + let left_num = self.get_side_free_number_value(&expr.left); + let right_num = self.get_side_free_number_value(&expr.right); + if let (Some(left_val), Some(right_val)) = (left_num, right_num) { + if left_val.fract() != 0.0 || right_val.fract() != 0.0 { + return None; + } + // only the lower 5 bits are used when shifting, so don't do anything + // if the shift amount is outside [0,32) + if !(0.0..32.0).contains(&right_val) { + return None; + } + let right_val_int = right_val as u32; + let bits = left_val.to_int_32(); + + let result_val: f64 = match expr.operator { + BinaryOperator::ShiftLeft => f64::from(bits.wrapping_shl(right_val_int)), + BinaryOperator::ShiftRight => f64::from(bits.wrapping_shr(right_val_int)), + BinaryOperator::ShiftRightZeroFill => { + // JavaScript always treats the result of >>> as unsigned. + // We must force Rust to do the same here. + let bits = bits as u32; + let res = bits.wrapping_shr(right_val_int); + f64::from(res) + } + _ => unreachable!(), + }; + return Some(ConstantValue::Number(result_val)); + } + None + } + _ => None, + } + } + + fn eval_logical_expression(&self, expr: &LogicalExpression<'a>) -> Option> { + match expr.operator { + LogicalOperator::And => { + if self.eval_to_boolean(&expr.left) == Some(true) { + self.eval_expression(&expr.right) + } else { + self.eval_expression(&expr.left) + } + } + _ => None, + } + } + + fn eval_unary_expression(&self, expr: &UnaryExpression<'a>) -> Option> { + match expr.operator { + UnaryOperator::Typeof => { + if !expr.argument.is_literal_value(true) { + return None; + } + let s = match &expr.argument { + Expression::FunctionExpression(_) => "function", + Expression::StringLiteral(_) => "string", + Expression::NumericLiteral(_) => "number", + Expression::BooleanLiteral(_) => "boolean", + Expression::NullLiteral(_) + | Expression::ObjectExpression(_) + | Expression::ArrayExpression(_) => "object", + Expression::UnaryExpression(e) if e.operator == UnaryOperator::Void => { + "undefined" + } + Expression::BigIntLiteral(_) => "bigint", + Expression::Identifier(ident) => match ident.name.as_str() { + "undefined" if self.is_global_reference(ident) => "undefined", + "NaN" | "Infinity" if self.is_global_reference(ident) => "number", + _ => return None, + }, + _ => return None, + }; + Some(ConstantValue::String(Cow::Borrowed(s))) + } + UnaryOperator::Void => { + if (!expr.argument.is_number() || !expr.argument.is_number_0()) + && !expr.may_have_side_effects() + { + return Some(ConstantValue::Undefined); + } + None + } + UnaryOperator::LogicalNot => { + // Don't fold !0 and !1 back to false. + if let Expression::NumericLiteral(n) = &expr.argument { + if n.value.is_zero() || n.value.is_one() { + return None; + } + } + self.eval_to_boolean(&expr.argument).map(|b| !b).map(ConstantValue::Boolean) + } + UnaryOperator::UnaryPlus => { + self.eval_to_number(&expr.argument).map(ConstantValue::Number) + } + UnaryOperator::UnaryNegation => { + let ty = ValueType::from(&expr.argument); + match ty { + ValueType::BigInt => { + self.eval_to_big_int(&expr.argument).map(|v| -v).map(ConstantValue::BigInt) + } + ValueType::Number => self + .eval_to_number(&expr.argument) + .map(|v| if v.is_nan() { v } else { -v }) + .map(ConstantValue::Number), + _ => None, + } + } + UnaryOperator::BitwiseNot => { + let ty = ValueType::from(&expr.argument); + match ty { + ValueType::BigInt => { + self.eval_to_big_int(&expr.argument).map(|v| !v).map(ConstantValue::BigInt) + } + #[expect(clippy::cast_lossless)] + ValueType::Number => self + .eval_to_number(&expr.argument) + .map(|v| !v.to_int_32()) + .map(|v| v as f64) + .map(ConstantValue::Number), + _ => None, + } + } + UnaryOperator::Delete => None, + } + } +} diff --git a/crates/oxc_minifier/src/value_type.rs b/crates/oxc_ecmascript/src/constant_evaluation/type.rs similarity index 94% rename from crates/oxc_minifier/src/value_type.rs rename to crates/oxc_ecmascript/src/constant_evaluation/type.rs index 24e4ae34e1c55..f5e5527c2c954 100644 --- a/crates/oxc_minifier/src/value_type.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/type.rs @@ -1,4 +1,4 @@ -use oxc_ast::ast::*; +use oxc_ast::ast::Expression; use oxc_syntax::operator::{BinaryOperator, UnaryOperator}; /// JavaScript Language Type @@ -16,6 +16,16 @@ pub enum ValueType { Undetermined, } +impl ValueType { + pub fn is_string(self) -> bool { + matches!(self, Self::String) + } + + pub fn is_number(self) -> bool { + matches!(self, Self::Number) + } +} + /// `get_known_value_type` /// /// Evaluate and attempt to determine which primitive value type it could resolve to. diff --git a/crates/oxc_ecmascript/src/constant_evaluation/value.rs b/crates/oxc_ecmascript/src/constant_evaluation/value.rs new file mode 100644 index 0000000000000..4f4f60a4e7b0e --- /dev/null +++ b/crates/oxc_ecmascript/src/constant_evaluation/value.rs @@ -0,0 +1,73 @@ +use std::borrow::Cow; + +use num_bigint::BigInt; + +use crate::ToJsString; + +#[derive(Debug, PartialEq)] +pub enum ConstantValue<'a> { + Number(f64), + BigInt(BigInt), + String(Cow<'a, str>), + Boolean(bool), + Undefined, +} + +impl<'a> ConstantValue<'a> { + pub fn is_number(&self) -> bool { + matches!(self, Self::Number(_)) + } + + pub fn is_big_int(&self) -> bool { + matches!(self, Self::BigInt(_)) + } + + pub fn is_string(&self) -> bool { + matches!(self, Self::String(_)) + } + + pub fn is_boolean(&self) -> bool { + matches!(self, Self::Boolean(_)) + } + + pub fn is_undefined(&self) -> bool { + matches!(self, Self::Undefined) + } + + pub fn into_string(self) -> Option> { + match self { + Self::String(s) => Some(s), + _ => None, + } + } + + pub fn into_number(self) -> Option { + match self { + Self::Number(s) => Some(s), + _ => None, + } + } + + pub fn into_boolean(self) -> Option { + match self { + Self::Boolean(s) => Some(s), + _ => None, + } + } +} + +impl<'a> ToJsString<'a> for ConstantValue<'a> { + fn to_js_string(&self) -> Option> { + match self { + Self::Number(n) => { + use oxc_syntax::number::ToJsString; + Some(Cow::Owned(n.to_js_string())) + } + // FIXME: to js number string + Self::BigInt(n) => Some(Cow::Owned(n.to_string() + "n")), + Self::String(s) => Some(s.clone()), + Self::Boolean(b) => Some(Cow::Borrowed(if *b { "true" } else { "false" })), + Self::Undefined => Some(Cow::Borrowed("undefined")), + } + } +} diff --git a/crates/oxc_ecmascript/src/lib.rs b/crates/oxc_ecmascript/src/lib.rs index 9985429bc5e91..eda232334fe4f 100644 --- a/crates/oxc_ecmascript/src/lib.rs +++ b/crates/oxc_ecmascript/src/lib.rs @@ -18,21 +18,16 @@ mod to_number; mod to_string; // Constant Evaluation -mod constant_evaluation; +pub mod constant_evaluation; + +// Side Effects +pub mod side_effects; pub use self::{ - bound_names::BoundNames, - constant_evaluation::{ConstantEvaluation, ConstantValue}, - is_simple_parameter_list::IsSimpleParameterList, - private_bound_identifiers::PrivateBoundIdentifiers, - prop_name::PropName, - string_char_at::StringCharAt, - string_index_of::StringIndexOf, - string_last_index_of::StringLastIndexOf, - string_to_big_int::StringToBigInt, - to_big_int::ToBigInt, - to_boolean::ToBoolean, - to_int_32::ToInt32, - to_number::ToNumber, + bound_names::BoundNames, is_simple_parameter_list::IsSimpleParameterList, + private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName, + string_char_at::StringCharAt, string_index_of::StringIndexOf, + string_last_index_of::StringLastIndexOf, string_to_big_int::StringToBigInt, + to_big_int::ToBigInt, to_boolean::ToBoolean, to_int_32::ToInt32, to_number::ToNumber, to_string::ToJsString, }; diff --git a/crates/oxc_minifier/src/node_util/check_for_state_change.rs b/crates/oxc_ecmascript/src/side_effects/check_for_state_change.rs similarity index 99% rename from crates/oxc_minifier/src/node_util/check_for_state_change.rs rename to crates/oxc_ecmascript/src/side_effects/check_for_state_change.rs index 16005303152a5..f4666b9ae8af9 100644 --- a/crates/oxc_minifier/src/node_util/check_for_state_change.rs +++ b/crates/oxc_ecmascript/src/side_effects/check_for_state_change.rs @@ -1,3 +1,4 @@ +#[allow(clippy::wildcard_imports)] use oxc_ast::ast::*; use oxc_syntax::operator::UnaryOperator; diff --git a/crates/oxc_minifier/src/node_util/may_have_side_effects.rs b/crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs similarity index 92% rename from crates/oxc_minifier/src/node_util/may_have_side_effects.rs rename to crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs index 194f8e16e423c..bd47317e2e478 100644 --- a/crates/oxc_minifier/src/node_util/may_have_side_effects.rs +++ b/crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs @@ -1,4 +1,4 @@ -use oxc_ast::ast::*; +use oxc_ast::ast::{Expression, ForStatementLeft, UnaryExpression}; use super::check_for_state_change::CheckForStateChange; diff --git a/crates/oxc_ecmascript/src/side_effects/mod.rs b/crates/oxc_ecmascript/src/side_effects/mod.rs new file mode 100644 index 0000000000000..2993d15e2bdb3 --- /dev/null +++ b/crates/oxc_ecmascript/src/side_effects/mod.rs @@ -0,0 +1,5 @@ +mod check_for_state_change; +mod may_have_side_effects; + +pub use check_for_state_change::CheckForStateChange; +pub use may_have_side_effects::MayHaveSideEffects; diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 8f683528f99cc..5a98d9def946c 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -6,6 +6,10 @@ use num_traits::Zero; use oxc_ast::ast::*; use oxc_ecmascript::ToInt32; +use oxc_ecmascript::{ + constant_evaluation::{IsLiteralValue, ValueType}, + side_effects::MayHaveSideEffects, +}; use oxc_span::{GetSpan, Span, SPAN}; use oxc_syntax::{ number::NumberBase, @@ -14,9 +18,8 @@ use oxc_syntax::{ use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; use crate::{ - node_util::{is_exact_int64, Ctx, IsLiteralValue, MayHaveSideEffects}, + node_util::{is_exact_int64, Ctx}, tri::Tri, - value_type::ValueType, CompressorPass, }; diff --git a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs index 6c81df5be80b7..85fe9dd7d9e91 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs @@ -1,10 +1,10 @@ use oxc_allocator::Vec; use oxc_ast::{ast::*, Visit}; -use oxc_ecmascript::ConstantEvaluation; +use oxc_ecmascript::constant_evaluation::{ConstantEvaluation, IsLiteralValue}; use oxc_span::SPAN; use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; -use crate::node_util::{Ctx, IsLiteralValue}; +use crate::node_util::Ctx; use crate::{keep_var::KeepVar, CompressorPass}; /// Remove Dead Code from the AST. diff --git a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs index 1a5de8ed0e6d1..858ca2c3cd76f 100644 --- a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs +++ b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs @@ -1,9 +1,10 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; +use oxc_ecmascript::side_effects::MayHaveSideEffects; use oxc_span::SPAN; use oxc_traverse::{Traverse, TraverseCtx}; -use crate::{node_util::MayHaveSideEffects, CompressorPass}; +use crate::CompressorPass; /// Statement Fusion /// diff --git a/crates/oxc_minifier/src/lib.rs b/crates/oxc_minifier/src/lib.rs index 241121a572e7f..da7841a567aed 100644 --- a/crates/oxc_minifier/src/lib.rs +++ b/crates/oxc_minifier/src/lib.rs @@ -8,7 +8,6 @@ mod keep_var; mod node_util; mod options; mod tri; -mod value_type; #[cfg(test)] mod tester; diff --git a/crates/oxc_minifier/src/node_util/mod.rs b/crates/oxc_minifier/src/node_util/mod.rs index 8a51528153f84..2786e4c0898d4 100644 --- a/crates/oxc_minifier/src/node_util/mod.rs +++ b/crates/oxc_minifier/src/node_util/mod.rs @@ -1,19 +1,13 @@ -mod check_for_state_change; -mod is_literal_value; -mod may_have_side_effects; - use std::borrow::Cow; use std::ops::Deref; use num_bigint::BigInt; use oxc_ast::ast::*; -use oxc_ecmascript::ConstantEvaluation; +use oxc_ecmascript::{constant_evaluation::ConstantEvaluation, side_effects::MayHaveSideEffects}; use oxc_ecmascript::{StringToBigInt, ToBigInt, ToJsString}; use oxc_semantic::{IsGlobalReference, SymbolTable}; use oxc_traverse::TraverseCtx; -pub use self::{is_literal_value::IsLiteralValue, may_have_side_effects::MayHaveSideEffects}; - #[derive(Clone, Copy)] pub struct Ctx<'a, 'b>(pub &'b TraverseCtx<'a>);