refactor(ecmascript): move ToInt32 from oxc_syntax to oxc_ecmascript (#6471)

This commit is contained in:
Boshen 2024-10-12 09:29:46 +00:00
parent 1ba2a247e1
commit 856cab5000
10 changed files with 122 additions and 120 deletions

View file

@ -4,8 +4,9 @@ mod bound_names;
mod is_simple_parameter_list;
mod private_bound_identifiers;
mod prop_name;
mod to_int_32;
pub use self::{
bound_names::BoundNames, is_simple_parameter_list::IsSimpleParameterList,
private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName,
private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName, to_int_32::ToInt32,
};

View file

@ -0,0 +1,83 @@
/// 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 ToInt32 {
fn to_int_32(&self) -> i32;
}
impl ToInt32 for f64 {
#[allow(clippy::float_cmp, clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
fn to_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
}
}

View file

@ -1,11 +1,13 @@
use rustc_hash::FxHashMap;
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use oxc_ecmascript::ToInt32;
use oxc_span::{Atom, GetSpan, SPAN};
use oxc_syntax::{
number::{NumberBase, ToJsInt32, ToJsString},
number::{NumberBase, ToJsString},
operator::{BinaryOperator, UnaryOperator},
};
use rustc_hash::FxHashMap;
use crate::{diagnostics::enum_member_initializers, IsolatedDeclarations};
@ -223,22 +225,22 @@ impl<'a> IsolatedDeclarations<'a> {
match expr.operator {
BinaryOperator::ShiftRight => Some(ConstantValue::Number(f64::from(
left.to_js_int_32().wrapping_shr(right.to_js_int_32() as u32),
left.to_int_32().wrapping_shr(right.to_int_32() as u32),
))),
BinaryOperator::ShiftRightZeroFill => Some(ConstantValue::Number(f64::from(
(left.to_js_int_32() as u32).wrapping_shr(right.to_js_int_32() as u32),
(left.to_int_32() as u32).wrapping_shr(right.to_int_32() as u32),
))),
BinaryOperator::ShiftLeft => Some(ConstantValue::Number(f64::from(
left.to_js_int_32().wrapping_shl(right.to_js_int_32() as u32),
left.to_int_32().wrapping_shl(right.to_int_32() as u32),
))),
BinaryOperator::BitwiseXOR => {
Some(ConstantValue::Number(f64::from(left.to_js_int_32() ^ right.to_js_int_32())))
Some(ConstantValue::Number(f64::from(left.to_int_32() ^ right.to_int_32())))
}
BinaryOperator::BitwiseOR => {
Some(ConstantValue::Number(f64::from(left.to_js_int_32() | right.to_js_int_32())))
Some(ConstantValue::Number(f64::from(left.to_int_32() | right.to_int_32())))
}
BinaryOperator::BitwiseAnd => {
Some(ConstantValue::Number(f64::from(left.to_js_int_32() & right.to_js_int_32())))
Some(ConstantValue::Number(f64::from(left.to_int_32() & right.to_int_32())))
}
BinaryOperator::Multiplication => Some(ConstantValue::Number(left * right)),
BinaryOperator::Division => Some(ConstantValue::Number(left / right)),
@ -276,9 +278,7 @@ impl<'a> IsolatedDeclarations<'a> {
match expr.operator {
UnaryOperator::UnaryPlus => Some(ConstantValue::Number(value)),
UnaryOperator::UnaryNegation => Some(ConstantValue::Number(-value)),
UnaryOperator::BitwiseNot => {
Some(ConstantValue::Number(f64::from(!value.to_js_int_32())))
}
UnaryOperator::BitwiseNot => Some(ConstantValue::Number(f64::from(!value.to_int_32()))),
_ => None,
}
}

View file

@ -3,10 +3,12 @@ use std::ops::Neg;
use num_bigint::BigInt;
use num_traits::Zero;
use oxc_ast::ast::*;
use oxc_ecmascript::ToInt32;
use oxc_span::{GetSpan, Span, SPAN};
use oxc_syntax::{
number::{NumberBase, ToJsInt32},
number::NumberBase,
operator::{BinaryOperator, LogicalOperator, UnaryOperator},
};
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
@ -195,7 +197,7 @@ impl<'a> PeepholeFoldConstants {
})
}
Expression::NumericLiteral(n) => is_valid(n.value).then(|| {
let value = !n.value.to_js_int_32();
let value = !n.value.to_int_32();
ctx.ast.expression_numeric_literal(
SPAN,
value.into(),
@ -231,7 +233,7 @@ impl<'a> PeepholeFoldConstants {
// `-~1` -> `2`
if let Expression::NumericLiteral(n) = &mut un.argument {
is_valid(n.value).then(|| {
let value = !n.value.to_js_int_32().wrapping_neg();
let value = !n.value.to_int_32().wrapping_neg();
ctx.ast.expression_numeric_literal(
SPAN,
value.into(),
@ -933,7 +935,7 @@ impl<'a> PeepholeFoldConstants {
#[allow(clippy::cast_sign_loss)]
let right_val_int = right_val as u32;
let bits = left_val.to_js_int_32();
let bits = left_val.to_int_32();
let result_val: f64 = match op {
BinaryOperator::ShiftLeft => f64::from(bits.wrapping_shl(right_val_int)),

View file

@ -1,4 +1,4 @@
use oxc_syntax::number::ToJsInt32;
use oxc_ecmascript::ToInt32;
pub(super) struct StringUtils;
@ -9,7 +9,7 @@ impl StringUtils {
search_value: Option<&str>,
from_index: Option<f64>,
) -> isize {
let from_index = from_index.map_or(0, |x| x.to_js_int_32().max(0)) as usize;
let from_index = from_index.map_or(0, |x| x.to_int_32().max(0)) as usize;
let Some(search_value) = search_value else {
return -1;
};
@ -25,8 +25,8 @@ impl StringUtils {
) -> isize {
let Some(search_value) = search_value else { return -1 };
let from_index = from_index
.map_or(usize::MAX, |x| x.to_js_int_32().max(0) as usize + search_value.len());
let from_index =
from_index.map_or(usize::MAX, |x| x.to_int_32().max(0) as usize + search_value.len());
string
.chars()

View file

@ -1,8 +1,8 @@
use oxc_allocator::Vec;
use oxc_ast::{ast::*, NONE};
use oxc_ecmascript::ToInt32;
use oxc_semantic::IsGlobalReference;
use oxc_span::{GetSpan, SPAN};
use oxc_syntax::number::ToJsInt32;
use oxc_syntax::{
number::NumberBase,
operator::{BinaryOperator, UnaryOperator},
@ -303,7 +303,7 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
let target = expr.left.as_simple_assignment_target_mut()?;
if matches!(expr.operator, AssignmentOperator::Subtraction) {
match &expr.right {
Expression::NumericLiteral(num) if num.value.to_js_int_32() == 1 => {
Expression::NumericLiteral(num) if num.value.to_int_32() == 1 => {
// The `_` will not be placed to the target code.
let target = std::mem::replace(
target,
@ -315,7 +315,7 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
if matches!(un.operator, UnaryOperator::UnaryNegation) =>
{
if let Expression::NumericLiteral(num) = &un.argument {
(num.value.to_js_int_32() == 1).then(|| {
(num.value.to_int_32() == 1).then(|| {
// The `_` will not be placed to the target code.
let target = std::mem::replace(
target,

View file

@ -193,7 +193,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Features
- f1ccbd4 syntax: Add `ToJsInt32` trait for f64 (#3132) (Boshen)
- f1ccbd4 syntax: Add `ToInt32` trait for f64 (#3132) (Boshen)
- 870d11f syntax: Add `ToJsString` trait for f64 (#3131) (Boshen)
- 46c02ae traverse: Add scope flags to `TraverseCtx` (#3229) (overlookmotel)

View file

@ -48,87 +48,3 @@ 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
}
}

View file

@ -753,7 +753,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Features
- f1ccbd4 syntax: Add `ToJsInt32` trait for f64 (#3132) (Boshen)
- f1ccbd4 syntax: Add `ToInt32` trait for f64 (#3132) (Boshen)
- 870d11f syntax: Add `ToJsString` trait for f64 (#3131) (Boshen)
- 34dd53c transformer: Report ambient module cannot be nested error (#3253) (Dunqing)
- 1b29e63 transformer: Do not elide jsx imports if a jsx element appears somewhere (#3237) (Dunqing)

View file

@ -1,15 +1,17 @@
use rustc_hash::FxHashMap;
use oxc_allocator::Vec;
use oxc_ast::{ast::*, visit::walk_mut, VisitMut, NONE};
use oxc_ecmascript::ToInt32;
use oxc_span::{Atom, Span, SPAN};
use oxc_syntax::{
node::NodeId,
number::{NumberBase, ToJsInt32, ToJsString},
number::{NumberBase, ToJsString},
operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator},
reference::ReferenceFlags,
symbol::SymbolFlags,
};
use oxc_traverse::{Traverse, TraverseCtx};
use rustc_hash::FxHashMap;
pub struct TypeScriptEnum<'a> {
enums: FxHashMap<Atom<'a>, FxHashMap<Atom<'a>, ConstantValue>>,
@ -475,22 +477,22 @@ impl<'a> TypeScriptEnum<'a> {
match expr.operator {
BinaryOperator::ShiftRight => Some(ConstantValue::Number(f64::from(
left.to_js_int_32().wrapping_shr(right.to_js_int_32() as u32),
left.to_int_32().wrapping_shr(right.to_int_32() as u32),
))),
BinaryOperator::ShiftRightZeroFill => Some(ConstantValue::Number(f64::from(
(left.to_js_int_32() as u32).wrapping_shr(right.to_js_int_32() as u32),
(left.to_int_32() as u32).wrapping_shr(right.to_int_32() as u32),
))),
BinaryOperator::ShiftLeft => Some(ConstantValue::Number(f64::from(
left.to_js_int_32().wrapping_shl(right.to_js_int_32() as u32),
left.to_int_32().wrapping_shl(right.to_int_32() as u32),
))),
BinaryOperator::BitwiseXOR => {
Some(ConstantValue::Number(f64::from(left.to_js_int_32() ^ right.to_js_int_32())))
Some(ConstantValue::Number(f64::from(left.to_int_32() ^ right.to_int_32())))
}
BinaryOperator::BitwiseOR => {
Some(ConstantValue::Number(f64::from(left.to_js_int_32() | right.to_js_int_32())))
Some(ConstantValue::Number(f64::from(left.to_int_32() | right.to_int_32())))
}
BinaryOperator::BitwiseAnd => {
Some(ConstantValue::Number(f64::from(left.to_js_int_32() & right.to_js_int_32())))
Some(ConstantValue::Number(f64::from(left.to_int_32() & right.to_int_32())))
}
BinaryOperator::Multiplication => Some(ConstantValue::Number(left * right)),
BinaryOperator::Division => Some(ConstantValue::Number(left / right)),
@ -527,9 +529,7 @@ impl<'a> TypeScriptEnum<'a> {
match expr.operator {
UnaryOperator::UnaryPlus => Some(ConstantValue::Number(value)),
UnaryOperator::UnaryNegation => Some(ConstantValue::Number(-value)),
UnaryOperator::BitwiseNot => {
Some(ConstantValue::Number(f64::from(!value.to_js_int_32())))
}
UnaryOperator::BitwiseNot => Some(ConstantValue::Number(f64::from(!value.to_int_32()))),
_ => None,
}
}