Skip to content

Commit

Permalink
Fix #9256
Browse files Browse the repository at this point in the history
  • Loading branch information
levi-nz committed Jul 16, 2024
1 parent 915bb98 commit 7e16e74
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ use swc_ecma_utils::{
StringType, SymbolType, UndefinedType, Value,
is_literal, number::JsNumber, prop_name_eq, to_int32, BoolType, ExprCtx, ExprExt, NullType,
NumberType, ObjectType, StringType, SymbolType, UndefinedType, Value,
is_literal,
number::{ToJsInt32, ToJsUint32},
prop_name_eq, to_int32, BoolType, ExprCtx, ExprExt, NullType, NumberType, ObjectType,
StringType, SymbolType, UndefinedType, Value,
};
use swc_ecma_visit::{as_folder, standard_only_visit_mut, VisitMut, VisitMutWith};
use Value::{Known, Unknown};
Expand Down
79 changes: 79 additions & 0 deletions crates/swc_ecma_utils/src/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,58 @@ impl std::ops::Not for JsNumber {

fn not(self) -> Self::Output {
JsNumber(!(self.as_int32()) as f64)
/// Implements [ToInt32] as defined in ECMAScript Section 9.5.
///
/// [ToInt32]: https://262.ecma-international.org/5.1/#sec-9.5
pub trait ToJsInt32 {
/// Converts `self` into a signed 32-bit integer as defined in
/// ECMAScript Section 9.5, [ToInt32].
///
/// [ToInt32]: https://262.ecma-international.org/5.1/#sec-9.5
fn to_js_int32(&self) -> i32;
}

impl ToJsInt32 for f64 {
fn to_js_int32(&self) -> i32 {
if self.is_nan() || self.is_infinite() || *self == 0.0 {
return 0;
}

const TWO_32: f64 = u32::MAX as f64 + 1.0; // 2^32
const TWO_31: f64 = i32::MAX as f64 + 1.0; // 2^31

let pos_int = self.signum() * self.abs().floor();
let int32_bit = pos_int % TWO_32;

(if int32_bit >= TWO_31 {
int32_bit - TWO_32
} else {
int32_bit
}) as i32
}
}

/// Implements [ToUint32] as defined in ECMAScript Section 9.6.
///
/// [ToUint32]: https://262.ecma-international.org/5.1/#sec-9.6
pub trait ToJsUint32 {
/// Converts `self` into an unsigned 32-bit integer as defined in
/// ECMAScript Section 9.6, [ToUint32].
///
/// [ToUint32]: https://262.ecma-international.org/5.1/#sec-9.6
fn to_js_uint32(&self) -> u32;
}

impl ToJsUint32 for f64 {
fn to_js_uint32(&self) -> u32 {
if self.is_nan() || self.is_infinite() || *self == 0.0 {
return 0;
}

const TWO_32: f64 = u32::MAX as f64 + 1.0; // 2^32

let pos_int = self.signum() * self.abs().floor();
(pos_int % TWO_32) as u32
}
}

Expand Down Expand Up @@ -318,5 +370,32 @@ mod test_js_number {
JsNumber(f64::NEG_INFINITY)
);
assert_eq!(JsNumber(-0.0) + JsNumber(0.0), JsNumber(-0.0));
mod tests {
use super::*;

#[test]
fn test_to_js_int32() {
assert_eq!(f64::NAN.to_js_int32(), 0);
assert_eq!(f64::INFINITY.to_js_int32(), 0);
assert_eq!(f64::NEG_INFINITY.to_js_int32(), 0);
assert_eq!(0.0.to_js_int32(), 0);

assert_eq!(f64::MIN.to_js_int32(), 0);
assert_eq!(f64::MAX.to_js_int32(), 0);

assert_eq!(5.2.to_js_int32(), 5);
}

#[test]
fn test_to_js_uint32() {
assert_eq!(f64::NAN.to_js_uint32(), 0);
assert_eq!(f64::INFINITY.to_js_uint32(), 0);
assert_eq!(f64::NEG_INFINITY.to_js_uint32(), 0);
assert_eq!(0.0.to_js_uint32(), 0);

assert_eq!(f64::MIN.to_js_uint32(), 0);
assert_eq!(f64::MAX.to_js_uint32(), 0);

assert_eq!(5.2.to_js_uint32(), 5);
}
}

0 comments on commit 7e16e74

Please sign in to comment.