mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(syntax): add ToJsInt32 trait for f64 (#3132)
This commit is contained in:
parent
870d11f1bb
commit
f1ccbd4856
4 changed files with 93 additions and 95 deletions
|
|
@ -40,3 +40,87 @@ impl ToJsString for f64 {
|
|||
buffer.format(*self).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a 64-bit floating point number to an `i32` according to the [`ToInt32`][ToInt32] algorithm.
|
||||
///
|
||||
/// [ToInt32]: https://tc39.es/ecma262/#sec-toint32
|
||||
///
|
||||
/// This is copied from [Boa](https://github.com/boa-dev/boa/blob/61567687cf4bfeca6bd548c3e72b6965e74b2461/core/engine/src/builtins/number/conversions.rs)
|
||||
pub trait ToJsInt32 {
|
||||
fn to_js_int_32(&self) -> i32;
|
||||
}
|
||||
|
||||
impl ToJsInt32 for f64 {
|
||||
#[allow(clippy::float_cmp, clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
|
||||
fn to_js_int_32(&self) -> i32 {
|
||||
const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
|
||||
const EXPONENT_MASK: u64 = 0x7FF0_0000_0000_0000;
|
||||
const SIGNIFICAND_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
|
||||
const HIDDEN_BIT: u64 = 0x0010_0000_0000_0000;
|
||||
const PHYSICAL_SIGNIFICAND_SIZE: i32 = 52; // Excludes the hidden bit.
|
||||
const SIGNIFICAND_SIZE: i32 = 53;
|
||||
|
||||
const EXPONENT_BIAS: i32 = 0x3FF + PHYSICAL_SIGNIFICAND_SIZE;
|
||||
const DENORMAL_EXPONENT: i32 = -EXPONENT_BIAS + 1;
|
||||
|
||||
fn is_denormal(number: f64) -> bool {
|
||||
(number.to_bits() & EXPONENT_MASK) == 0
|
||||
}
|
||||
|
||||
fn exponent(number: f64) -> i32 {
|
||||
if is_denormal(number) {
|
||||
return DENORMAL_EXPONENT;
|
||||
}
|
||||
|
||||
let d64 = number.to_bits();
|
||||
let biased_e = ((d64 & EXPONENT_MASK) >> PHYSICAL_SIGNIFICAND_SIZE) as i32;
|
||||
|
||||
biased_e - EXPONENT_BIAS
|
||||
}
|
||||
|
||||
fn significand(number: f64) -> u64 {
|
||||
let d64 = number.to_bits();
|
||||
let significand = d64 & SIGNIFICAND_MASK;
|
||||
|
||||
if is_denormal(number) {
|
||||
significand
|
||||
} else {
|
||||
significand + HIDDEN_BIT
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(number: f64) -> i64 {
|
||||
if (number.to_bits() & SIGN_MASK) == 0 {
|
||||
1
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
let number = *self;
|
||||
|
||||
if number.is_finite() && number <= f64::from(i32::MAX) && number >= f64::from(i32::MIN) {
|
||||
let i = number as i32;
|
||||
if f64::from(i) == number {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
let exponent = exponent(number);
|
||||
let bits = if exponent < 0 {
|
||||
if exponent <= -SIGNIFICAND_SIZE {
|
||||
return 0;
|
||||
}
|
||||
|
||||
significand(number) >> -exponent
|
||||
} else {
|
||||
if exponent > 31 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(significand(number) << exponent) & 0xFFFF_FFFF
|
||||
};
|
||||
|
||||
(sign(number) * (bits as i64)) as i32
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
/// This file copied from [Boa](https://github.com/boa-dev/boa/blob/61567687cf4bfeca6bd548c3e72b6965e74b2461/core/engine/src/builtins/number/conversions.rs)
|
||||
|
||||
/// Converts a 64-bit floating point number to an `i32` according to the [`ToInt32`][ToInt32] algorithm.
|
||||
///
|
||||
/// [ToInt32]: https://tc39.es/ecma262/#sec-toint32
|
||||
#[allow(clippy::float_cmp, clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
|
||||
pub(crate) fn f64_to_int32(number: f64) -> i32 {
|
||||
const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
|
||||
const EXPONENT_MASK: u64 = 0x7FF0_0000_0000_0000;
|
||||
const SIGNIFICAND_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
|
||||
const HIDDEN_BIT: u64 = 0x0010_0000_0000_0000;
|
||||
const PHYSICAL_SIGNIFICAND_SIZE: i32 = 52; // Excludes the hidden bit.
|
||||
const SIGNIFICAND_SIZE: i32 = 53;
|
||||
|
||||
const EXPONENT_BIAS: i32 = 0x3FF + PHYSICAL_SIGNIFICAND_SIZE;
|
||||
const DENORMAL_EXPONENT: i32 = -EXPONENT_BIAS + 1;
|
||||
|
||||
fn is_denormal(number: f64) -> bool {
|
||||
(number.to_bits() & EXPONENT_MASK) == 0
|
||||
}
|
||||
|
||||
fn exponent(number: f64) -> i32 {
|
||||
if is_denormal(number) {
|
||||
return DENORMAL_EXPONENT;
|
||||
}
|
||||
|
||||
let d64 = number.to_bits();
|
||||
let biased_e = ((d64 & EXPONENT_MASK) >> PHYSICAL_SIGNIFICAND_SIZE) as i32;
|
||||
|
||||
biased_e - EXPONENT_BIAS
|
||||
}
|
||||
|
||||
fn significand(number: f64) -> u64 {
|
||||
let d64 = number.to_bits();
|
||||
let significand = d64 & SIGNIFICAND_MASK;
|
||||
|
||||
if is_denormal(number) {
|
||||
significand
|
||||
} else {
|
||||
significand + HIDDEN_BIT
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(number: f64) -> i64 {
|
||||
if (number.to_bits() & SIGN_MASK) == 0 {
|
||||
1
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
if number.is_finite() && number <= f64::from(i32::MAX) && number >= f64::from(i32::MIN) {
|
||||
let i = number as i32;
|
||||
if f64::from(i) == number {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
let exponent = exponent(number);
|
||||
let bits = if exponent < 0 {
|
||||
if exponent <= -SIGNIFICAND_SIZE {
|
||||
return 0;
|
||||
}
|
||||
|
||||
significand(number) >> -exponent
|
||||
} else {
|
||||
if exponent > 31 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(significand(number) << exponent) & 0xFFFF_FFFF
|
||||
};
|
||||
|
||||
(sign(number) * (bits as i64)) as i32
|
||||
}
|
||||
|
||||
/// Converts a 64-bit floating point number to an `u32` according to the [`ToUint32`][ToUint32] algorithm.
|
||||
///
|
||||
/// [ToUint32]: https://tc39.es/ecma262/#sec-touint32
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
pub(crate) fn f64_to_uint32(number: f64) -> u32 {
|
||||
f64_to_int32(number) as u32
|
||||
}
|
||||
|
|
@ -4,15 +4,13 @@ use oxc_allocator::{Box, Vec};
|
|||
use oxc_ast::{ast::*, visit::walk_mut, VisitMut};
|
||||
use oxc_span::{Atom, SPAN};
|
||||
use oxc_syntax::{
|
||||
number::{NumberBase, ToJsString},
|
||||
number::{NumberBase, ToJsInt32, ToJsString},
|
||||
operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator},
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::context::Ctx;
|
||||
|
||||
use super::conversions::{f64_to_int32, f64_to_uint32};
|
||||
|
||||
pub struct TypeScriptEnum<'a> {
|
||||
ctx: Ctx<'a>,
|
||||
enums: FxHashMap<Atom<'a>, FxHashMap<Atom<'a>, ConstantValue>>,
|
||||
|
|
@ -376,7 +374,7 @@ impl<'a> TypeScriptEnum<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss, clippy::cast_sign_loss)]
|
||||
fn eval_binary_expression(
|
||||
&self,
|
||||
expr: &BinaryExpression<'a>,
|
||||
|
|
@ -414,22 +412,22 @@ impl<'a> TypeScriptEnum<'a> {
|
|||
|
||||
match expr.operator {
|
||||
BinaryOperator::ShiftRight => Some(ConstantValue::Number(f64::from(
|
||||
f64_to_int32(left).wrapping_shr(f64_to_uint32(right)),
|
||||
left.to_js_int_32().wrapping_shr(right.to_js_int_32() as u32),
|
||||
))),
|
||||
BinaryOperator::ShiftRightZeroFill => Some(ConstantValue::Number(f64::from(
|
||||
f64_to_uint32(left).wrapping_shr(f64_to_uint32(right)),
|
||||
(left.to_js_int_32() as u32).wrapping_shr(right.to_js_int_32() as u32),
|
||||
))),
|
||||
BinaryOperator::ShiftLeft => Some(ConstantValue::Number(f64::from(
|
||||
f64_to_int32(left).wrapping_shl(f64_to_uint32(right)),
|
||||
left.to_js_int_32().wrapping_shl(right.to_js_int_32() as u32),
|
||||
))),
|
||||
BinaryOperator::BitwiseXOR => {
|
||||
Some(ConstantValue::Number(f64::from(f64_to_int32(left) ^ f64_to_int32(right))))
|
||||
Some(ConstantValue::Number(f64::from(left.to_js_int_32() ^ right.to_js_int_32())))
|
||||
}
|
||||
BinaryOperator::BitwiseOR => {
|
||||
Some(ConstantValue::Number(f64::from(f64_to_int32(left) | f64_to_int32(right))))
|
||||
Some(ConstantValue::Number(f64::from(left.to_js_int_32() | right.to_js_int_32())))
|
||||
}
|
||||
BinaryOperator::BitwiseAnd => {
|
||||
Some(ConstantValue::Number(f64::from(f64_to_int32(left) & f64_to_int32(right))))
|
||||
Some(ConstantValue::Number(f64::from(left.to_js_int_32() & right.to_js_int_32())))
|
||||
}
|
||||
BinaryOperator::Multiplication => Some(ConstantValue::Number(left * right)),
|
||||
BinaryOperator::Division => Some(ConstantValue::Number(left / right)),
|
||||
|
|
@ -467,7 +465,7 @@ impl<'a> TypeScriptEnum<'a> {
|
|||
UnaryOperator::UnaryPlus => Some(ConstantValue::Number(value)),
|
||||
UnaryOperator::UnaryNegation => Some(ConstantValue::Number(-value)),
|
||||
UnaryOperator::BitwiseNot => {
|
||||
Some(ConstantValue::Number(f64::from(!f64_to_int32(value))))
|
||||
Some(ConstantValue::Number(f64::from(!value.to_js_int_32())))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
mod annotations;
|
||||
mod collector;
|
||||
mod conversions;
|
||||
mod diagnostics;
|
||||
mod r#enum;
|
||||
mod module;
|
||||
|
|
|
|||
Loading…
Reference in a new issue