fix(minifier): do not fold object comparisons (#8375)

This commit is contained in:
Boshen 2025-01-09 05:47:35 +00:00
parent a1752a062c
commit 5516f7fcb9
2 changed files with 38 additions and 37 deletions

View file

@ -266,20 +266,18 @@ pub trait ConstantEvaluation<'a> {
_ => unreachable!(),
}))
}
BinaryOperator::LessThan => {
self.is_less_than(left, right, true).map(|value| match value {
ConstantValue::Undefined => ConstantValue::Boolean(false),
_ => value,
})
}
BinaryOperator::LessThan => self.is_less_than(left, right).map(|value| match value {
ConstantValue::Undefined => ConstantValue::Boolean(false),
_ => value,
}),
BinaryOperator::GreaterThan => {
self.is_less_than(right, left, false).map(|value| match value {
self.is_less_than(right, left).map(|value| match value {
ConstantValue::Undefined => ConstantValue::Boolean(false),
_ => value,
})
}
BinaryOperator::LessEqualThan => {
self.is_less_than(right, left, false).map(|value| match value {
self.is_less_than(right, left).map(|value| match value {
ConstantValue::Boolean(true) | ConstantValue::Undefined => {
ConstantValue::Boolean(false)
}
@ -288,7 +286,7 @@ pub trait ConstantEvaluation<'a> {
})
}
BinaryOperator::GreaterEqualThan => {
self.is_less_than(left, right, true).map(|value| match value {
self.is_less_than(left, right).map(|value| match value {
ConstantValue::Boolean(true) | ConstantValue::Undefined => {
ConstantValue::Boolean(false)
}
@ -444,42 +442,37 @@ pub trait ConstantEvaluation<'a> {
}
/// <https://tc39.es/ecma262/#sec-abstract-relational-comparison>
fn is_less_than(
&self,
left_expr: &Expression<'a>,
right_expr: &Expression<'a>,
_left_first: bool,
) -> Option<ConstantValue<'a>> {
let left = ValueType::from(left_expr);
let right = ValueType::from(right_expr);
if left.is_string() && right.is_string() {
let left_string = self.get_side_free_string_value(left_expr);
let right_string = self.get_side_free_string_value(right_expr);
if let (Some(left_string), Some(right_string)) = (left_string, right_string) {
// In JS, browsers parse \v differently. So do not compare strings if one contains \v.
if left_string.contains('\u{000B}') || right_string.contains('\u{000B}') {
return None;
}
return Some(ConstantValue::Boolean(
left_string.cmp(&right_string) == Ordering::Less,
));
}
}
fn is_less_than(&self, x: &Expression<'a>, y: &Expression<'a>) -> Option<ConstantValue<'a>> {
// a. Let px be ? ToPrimitive(x, number).
// b. Let py be ? ToPrimitive(y, number).
let px = ValueType::from(x);
// `.toString()` method is called and compared.
// TODO: bigint is handled very differently in the spec
// See <https://tc39.es/ecma262/#sec-islessthan>
if left.is_bigint() || right.is_bigint() {
if px.is_object() || px.is_undetermined() || px.is_bigint() {
return None;
}
let left_num = self.get_side_free_number_value(left_expr)?;
let right_num = self.get_side_free_number_value(right_expr)?;
let py = ValueType::from(y);
if left_num.is_nan() || right_num.is_nan() {
return Some(ConstantValue::Undefined);
if py.is_object() || py.is_undetermined() || py.is_bigint() {
return None;
}
if px.is_string() && py.is_string() {
let left_string = self.get_side_free_string_value(x)?;
let right_string = self.get_side_free_string_value(y)?;
return Some(ConstantValue::Boolean(left_string.cmp(&right_string) == Ordering::Less));
}
let left_num = self.get_side_free_number_value(x)?;
if left_num.is_nan() {
return Some(ConstantValue::Undefined);
}
let right_num = self.get_side_free_number_value(y)?;
if right_num.is_nan() {
return Some(ConstantValue::Undefined);
}
Some(ConstantValue::Boolean(left_num < right_num))
}
}

View file

@ -715,6 +715,14 @@ mod test {
#[test]
fn test_comparison() {
test("(1, 2) !== 2", "false");
test_same("({} <= {})");
test_same("({} >= {})");
test_same("({} > {})");
test_same("({} < {})");
test_same("([] <= [])");
test_same("([] >= [])");
test_same("([] > [])");
test_same("([] < [])");
}
#[test]