refactor(ecmascript): add ToUint32 trait (#13272)

Added `ToUint32` trait that corresponds to the [`ToUint32` abstract operation](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-touint32).
This commit is contained in:
sapphi-red committed 2025-08-24 01:08:39 +00:00
1 parent fd14b39fa9
commit 66a5673cd9
5 files changed
+38 -20

No files matched your search

@@ -20,7 +20,7 @@ use oxc_ast::{AstBuilder, ast::*};
use equality_comparison::{abstract_equality_comparison, strict_equality_comparison};
use crate::{
ToBigInt, ToBoolean, ToInt32, ToJsString as ToJsStringTrait, ToNumber,
ToBigInt, ToBoolean, ToInt32, ToJsString as ToJsStringTrait, ToNumber, ToUint32,
is_less_than::is_less_than,
side_effects::{MayHaveSideEffects, MayHaveSideEffectsContext},
to_numeric::ToNumeric,
@@ -265,29 +265,26 @@ fn binary_operation_evaluate_value_to<'a>(
}
Some(ConstantValue::Number(result))
}
#[expect(clippy::cast_sign_loss)]
BinaryOperator::ShiftLeft => {
let left = left.evaluate_value_to_number(ctx)?;
let right = right.evaluate_value_to_number(ctx)?;
let left = left.to_int_32();
let right = (right.to_int_32() as u32) & 31;
let right = right.to_uint_32() & 31;
Some(ConstantValue::Number(f64::from(left << right)))
}
#[expect(clippy::cast_sign_loss)]
BinaryOperator::ShiftRight => {
let left = left.evaluate_value_to_number(ctx)?;
let right = right.evaluate_value_to_number(ctx)?;
let left = left.to_int_32();
let right = (right.to_int_32() as u32) & 31;
let right = right.to_uint_32() & 31;
Some(ConstantValue::Number(f64::from(left >> right)))
}
#[expect(clippy::cast_sign_loss)]
BinaryOperator::ShiftRightZeroFill => {
let left = left.evaluate_value_to_number(ctx)?;
let right = right.evaluate_value_to_number(ctx)?;
let left = left.to_int_32();
let right = (right.to_int_32() as u32) & 31;
Some(ConstantValue::Number(f64::from((left as u32) >> right)))
let left = left.to_uint_32();
let right = right.to_uint_32() & 31;
Some(ConstantValue::Number(f64::from(left >> right)))
}
BinaryOperator::LessThan => is_less_than(ctx, left, right).map(|value| match value {
ConstantValue::Undefined => ConstantValue::Boolean(false),
+1 -1
View File
@@ -48,7 +48,7 @@ pub use self::{
string_to_number::StringToNumber,
to_big_int::ToBigInt,
to_boolean::ToBoolean,
to_int_32::ToInt32,
to_int_32::{ToInt32, ToUint32},
to_integer_index::ToIntegerIndex,
to_number::ToNumber,
to_primitive::ToPrimitive,
+23
View File
@@ -7,6 +7,13 @@ pub trait ToInt32 {
fn to_int_32(&self) -> i32;
}
pub trait ToUint32: ToInt32 {
fn to_uint_32(&self) -> u32 {
self.to_int_32().cast_unsigned()
}
}
impl<T> ToUint32 for T where T: ToInt32 {}
impl ToInt32 for f64 {
fn to_int_32(&self) -> i32 {
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
@@ -191,4 +198,20 @@ mod test {
}
}
}
#[test]
#[expect(clippy::cast_precision_loss)]
fn f64_to_uint32_conversion() {
assert_eq!(0.0_f64.to_uint_32(), 0);
assert_eq!((-0.0_f64).to_uint_32(), 0);
assert_eq!(f64::NAN.to_uint_32(), 0);
assert_eq!(f64::INFINITY.to_uint_32(), 0);
assert_eq!(f64::NEG_INFINITY.to_uint_32(), 0);
assert_eq!(((i64::from(u32::MAX) + 1) as f64).to_uint_32(), u32::MIN);
assert_eq!(((i64::from(u32::MIN) - 1) as f64).to_uint_32(), u32::MAX);
// Test edge cases with maximum safe integers
assert_eq!((9_007_199_254_740_992.0_f64).to_uint_32(), 0); // 2^53
assert_eq!((-9_007_199_254_740_992.0_f64).to_uint_32(), 0); // -2^53
}
}
+4 -5
View File
@@ -2,7 +2,7 @@ use rustc_hash::FxHashMap;
use oxc_allocator::CloneIn;
use oxc_ast::ast::*;
use oxc_ecmascript::ToInt32;
use oxc_ecmascript::{ToInt32, ToUint32};
use oxc_span::{Atom, GetSpan, SPAN};
use oxc_syntax::{
number::{NumberBase, ToJsString},
@@ -204,16 +204,15 @@ impl<'a> IsolatedDeclarations<'a> {
ConstantValue::String(_) => return None,
};
#[expect(clippy::cast_sign_loss)]
match expr.operator {
BinaryOperator::ShiftRight => Some(ConstantValue::Number(f64::from(
left.to_int_32().wrapping_shr(right.to_int_32() as u32),
left.to_int_32().wrapping_shr(right.to_uint_32()),
))),
BinaryOperator::ShiftRightZeroFill => Some(ConstantValue::Number(f64::from(
(left.to_int_32() as u32).wrapping_shr(right.to_int_32() as u32),
(left.to_uint_32()).wrapping_shr(right.to_uint_32()),
))),
BinaryOperator::ShiftLeft => Some(ConstantValue::Number(f64::from(
left.to_int_32().wrapping_shl(right.to_int_32() as u32),
left.to_int_32().wrapping_shl(right.to_uint_32()),
))),
BinaryOperator::BitwiseXOR => {
Some(ConstantValue::Number(f64::from(left.to_int_32() ^ right.to_int_32())))
@@ -6,7 +6,7 @@ use oxc_allocator::{StringBuilder, TakeIn, Vec as ArenaVec};
use oxc_ast::{NONE, ast::*};
use oxc_ast_visit::{VisitMut, walk_mut};
use oxc_data_structures::stack::NonEmptyStack;
use oxc_ecmascript::ToInt32;
use oxc_ecmascript::{ToInt32, ToUint32};
use oxc_semantic::{ScopeFlags, ScopeId};
use oxc_span::{Atom, SPAN, Span};
use oxc_syntax::{
@@ -444,7 +444,6 @@ impl<'a> TypeScriptEnum<'a> {
}
}
#[expect(clippy::cast_sign_loss)]
fn eval_binary_expression(
&self,
expr: &BinaryExpression<'a>,
@@ -485,13 +484,13 @@ impl<'a> TypeScriptEnum<'a> {
match expr.operator {
BinaryOperator::ShiftRight => Some(ConstantValue::Number(f64::from(
left.to_int_32().wrapping_shr(right.to_int_32() as u32),
left.to_int_32().wrapping_shr(right.to_uint_32()),
))),
BinaryOperator::ShiftRightZeroFill => Some(ConstantValue::Number(f64::from(
(left.to_int_32() as u32).wrapping_shr(right.to_int_32() as u32),
(left.to_uint_32()).wrapping_shr(right.to_uint_32()),
))),
BinaryOperator::ShiftLeft => Some(ConstantValue::Number(f64::from(
left.to_int_32().wrapping_shl(right.to_int_32() as u32),
left.to_int_32().wrapping_shl(right.to_uint_32()),
))),
BinaryOperator::BitwiseXOR => {
Some(ConstantValue::Number(f64::from(left.to_int_32() ^ right.to_int_32())))