Skip to content

Commit

Permalink
refactor(ecmascript): move ToInt32 from oxc_syntax to `oxc_ecmascri…
Browse files Browse the repository at this point in the history
…pt` (#6471)
  • Loading branch information
Boshen committed Oct 12, 2024
1 parent 1ba2a24 commit 856cab5
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 120 deletions.
3 changes: 2 additions & 1 deletion crates/oxc_ecmascript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 83 additions & 0 deletions crates/oxc_ecmascript/src/to_int_32.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
22 changes: 11 additions & 11 deletions crates/oxc_isolated_declarations/src/enum.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -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,
}
}
Expand Down
10 changes: 6 additions & 4 deletions crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use oxc_syntax::number::ToJsInt32;
use oxc_ecmascript::ToInt32;

pub(super) struct StringUtils;

Expand All @@ -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;
};
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_syntax/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
84 changes: 0 additions & 84 deletions crates/oxc_syntax/src/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
2 changes: 1 addition & 1 deletion crates/oxc_transformer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 856cab5

Please sign in to comment.