mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(ecmascript): move ToInt32 from oxc_syntax to oxc_ecmascript (#6471)
This commit is contained in:
parent
1ba2a247e1
commit
856cab5000
10 changed files with 122 additions and 120 deletions
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
83
crates/oxc_ecmascript/src/to_int_32.rs
Normal file
83
crates/oxc_ecmascript/src/to_int_32.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue