fix(es/minifier): Fix evaluation of -0 as a string (#9011)

**Related issue:**

 - Closes #9010

---------

Co-authored-by: magic-akari <akari.ccino@gmail.com>
This commit is contained in:
Donny/강동윤 2024-06-02 19:32:44 +09:00 committed by GitHub
parent 1f911f9d19
commit 9f8e24a76c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 40 additions and 12 deletions

5
Cargo.lock generated
View File

@ -3225,9 +3225,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "ryu-js"
@ -4896,6 +4896,7 @@ dependencies = [
"once_cell",
"rayon",
"rustc-hash",
"ryu-js",
"stacker",
"swc_atoms",
"swc_common",

View File

@ -1,7 +1,7 @@
use radix_fmt::Radix;
use swc_common::{util::take::Take, Spanned, SyntaxContext};
use swc_ecma_ast::*;
use swc_ecma_utils::{undefined, ExprExt, IsEmpty, Value};
use swc_ecma_utils::{number::ToJsString, undefined, ExprExt, IsEmpty, Value};
use super::Pure;
use crate::compress::util::{eval_as_number, is_pure_undefined_or_null};
@ -382,7 +382,7 @@ impl Pure<'_> {
if args.first().is_none() {
// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.prototype.toprecision
// 2. If precision is undefined, return ! ToString(x).
let value = ryu_js::Buffer::new().format(num.value).to_string().into();
let value = num.value.to_js_string().into();
self.changed = true;
report_change!(
@ -478,7 +478,7 @@ impl Pure<'_> {
.map_or(Some(10f64), |arg| eval_as_number(&self.expr_ctx, &arg.expr))
{
if base.trunc() == 10. {
let value = ryu_js::Buffer::new().format(num.value).to_string().into();
let value = num.value.to_js_string().into();
*e = Expr::Lit(Lit::Str(Str {
span: e.span(),
raw: None,

View File

@ -11316,3 +11316,12 @@ fn issue_8982_2() {
",
);
}
#[test]
fn issue_9010() {
run_default_exec_test(
r#"
console.log(-0 + [])
"#,
);
}

View File

@ -3,7 +3,7 @@ use std::mem;
use swc_atoms::JsWord;
use swc_common::{collections::AHashMap, Mark, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::{undefined, ExprFactory};
use swc_ecma_utils::{number::ToJsString, undefined, ExprFactory};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -207,14 +207,12 @@ impl<'a> EnumValueComputer<'a> {
TsEnumRecordValue::String(format!("{}{}", left, right).into())
}
(TsEnumRecordValue::Number(left), TsEnumRecordValue::String(right), op!(bin, "+")) => {
let mut buffer = ryu_js::Buffer::new();
let left = buffer.format(left);
let left = left.to_js_string();
TsEnumRecordValue::String(format!("{}{}", left, right).into())
}
(TsEnumRecordValue::String(left), TsEnumRecordValue::Number(right), op!(bin, "+")) => {
let mut buffer = ryu_js::Buffer::new();
let right = buffer.format(right);
let right = right.to_js_string();
TsEnumRecordValue::String(format!("{}{}", left, right).into())
}
@ -279,7 +277,7 @@ impl<'a> EnumValueComputer<'a> {
let expr = match expr {
TsEnumRecordValue::String(s) => s.to_string(),
TsEnumRecordValue::Number(n) => ryu_js::Buffer::new().format(n).to_string(),
TsEnumRecordValue::Number(n) => n.to_js_string(),
_ => return opaque_expr,
};

View File

@ -25,6 +25,7 @@ num_cpus = { workspace = true }
once_cell = { workspace = true }
rayon = { workspace = true, optional = true }
rustc-hash = { workspace = true }
ryu-js = { workspace = true }
tracing = { workspace = true }
unicode-id = { workspace = true }

View File

@ -13,6 +13,7 @@ pub extern crate swc_common;
use std::{borrow::Cow, hash::Hash, num::FpCategory, ops::Add};
use number::ToJsString;
use rustc_hash::FxHashMap;
use swc_atoms::JsWord;
use swc_common::{
@ -50,6 +51,7 @@ mod value;
pub mod var;
mod node_ignore_span;
pub mod number;
pub mod stack_size;
pub use node_ignore_span::NodeIgnoringSpan;
@ -979,7 +981,13 @@ pub trait ExprExt {
match *expr {
Expr::Lit(ref l) => match *l {
Lit::Str(Str { ref value, .. }) => Known(Cow::Borrowed(value)),
Lit::Num(ref n) => Known(format!("{}", n).into()),
Lit::Num(ref n) => {
if n.value == -0.0 {
return Known(Cow::Borrowed("0"));
}
Known(Cow::Owned(n.value.to_js_string()))
}
Lit::Bool(Bool { value: true, .. }) => Known(Cow::Borrowed("true")),
Lit::Bool(Bool { value: false, .. }) => Known(Cow::Borrowed("false")),
Lit::Null(..) => Known(Cow::Borrowed("null")),

View File

@ -0,0 +1,11 @@
/// <https://tc39.es/ecma262/#sec-numeric-types-number-tostring>
pub trait ToJsString {
fn to_js_string(&self) -> String;
}
impl ToJsString for f64 {
fn to_js_string(&self) -> String {
let mut buffer = ryu_js::Buffer::new();
buffer.format(*self).to_string()
}
}