Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(linter/tree-shaking): correct the calculation of >>, << and >>> #4932

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/oxc_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ oxc_sourcemap = { workspace = true }
oxc_mangler = { workspace = true }
oxc_index = { workspace = true }

bitflags = { workspace = true }
nonmax = { workspace = true }
once_cell = { workspace = true }
daachorse = { workspace = true }
bitflags = { workspace = true }
nonmax = { workspace = true }
once_cell = { workspace = true }
daachorse = { workspace = true }

[dev-dependencies]
oxc_parser = { workspace = true }
Expand Down
61 changes: 47 additions & 14 deletions crates/oxc_linter/src/utils/tree_shaking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,12 +420,19 @@ pub fn calculate_binary_operation(op: BinaryOperator, left: Value, right: Value)
}

BinaryOperator::ShiftRightZeroFill => match (left, right) {
// <https://tc39.es/ecma262/#sec-numeric-types-number-unsignedRightShift>
(Value::Number(a), Value::Number(b)) => {
if b >= a {
Value::Number(0.0)
} else {
Value::Number(f64::from((a as u32) >> (b as u32)))
}
// Casting between two integers of the same size (e.g. i32 -> u32) is a no-op
let a = a as i32;
let b = b as i32;
// 1. Let lNum be ! ToUint32(x).
let l_num = a as u32;
// 2. Let rNum be ! ToUint32(y).
let r_num = b as u32;
// 3. Let shiftCount be ℝ(rNum) modulo 32.
let shift_count = r_num % 32;
// 4. Return the result of performing a zero-filling right shift of lNum by shiftCount bits.
Value::Number(f64::from(l_num >> shift_count))
}
_ => Value::Unknown,
},
Expand All @@ -450,18 +457,33 @@ pub fn calculate_binary_operation(op: BinaryOperator, left: Value, right: Value)
_ => Value::Unknown,
},
BinaryOperator::ShiftLeft => match (left, right) {
// <https://tc39.es/ecma262/#sec-numeric-types-number-leftShift>
(Value::Number(a), Value::Number(b)) => {
// NOTE: could overflow, in which case node produces `a`
if b >= 32.0 {
Value::Number(a)
} else {
Value::Number(f64::from((a as i32) << (b as i32)))
}
// 1. Let lNum be ! ToInt32(x).
let l_num = a as i32;
// 2. Let rNum be ! ToUint32(y).
let r_num = b as i32;
let r_num = r_num as u32;
// 3. Let shiftCount be ℝ(rNum) modulo 32.
let shift_count = r_num % 32;
// 4. Return the result of left shifting lNum by shiftCount bits.
Value::Number(f64::from(l_num << shift_count))
}
_ => Value::Unknown,
},
BinaryOperator::ShiftRight => match (left, right) {
(Value::Number(a), Value::Number(b)) => Value::Number(f64::from(a as i32 >> b as i32)),
// <https://tc39.es/ecma262/#sec-numeric-types-number-signedRightShift>
(Value::Number(a), Value::Number(b)) => {
// 1. Let lNum be ! ToInt32(x).
let l_num = a as i32;
// 2. Let rNum be ! ToUint32(y).
let r_num = b as i32;
let r_num = r_num as u32;
// 3. Let shiftCount be ℝ(rNum) modulo 32.
let shift_count = r_num % 32;
// 4. Return the result of performing a sign-extending right shift of lNum by shiftCount bits. The most significant bit is propagated. The mathematical value of the result is exactly representable as a 32-bit two's complement bit string.i
Value::Number(f64::from(l_num >> shift_count))
}
_ => Value::Unknown,
},
BinaryOperator::In | BinaryOperator::Instanceof => Value::Unknown,
Expand Down Expand Up @@ -601,19 +623,30 @@ fn test_calculate_binary_operation() {
assert_eq!(fun(op, Value::Number(1.0), Value::Number(0.0),), Value::Number(1.0));
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Number(4.0));
assert_eq!(fun(op, Value::Number(1.0), Value::Number(31.0),), Value::Number(-2_147_483_648.0));
assert_eq!(fun(op, Value::Number(1.0), Value::Number(f64::MAX),), Value::Number(1.0));
assert_eq!(
fun(op, Value::Number(1.0), Value::Number(f64::MAX),),
Value::Number(-2_147_483_648.0)
);
assert_eq!(fun(op, Value::Number(-5.0), Value::Number(2.0),), Value::Number(-20.0));

// ">>",
let op = BinaryOperator::ShiftRight;
assert_eq!(fun(op, Value::Number(4.0), Value::Number(2.0),), Value::Number(1.0));
// issue: https://github.com/oxc-project/oxc/issues/4914
assert_eq!(fun(op, Value::Number(4.0), Value::Number(49.0)), Value::Number(0.0));
assert_eq!(fun(op, Value::Number(-5.0), Value::Number(2.0),), Value::Number(-2.0));

// ">>>",
let op = BinaryOperator::ShiftRightZeroFill;
assert_eq!(fun(op, Value::Number(0.0), Value::Number(0.0),), Value::Number(0.0));
assert_eq!(fun(op, Value::Number(1.0), Value::Number(0.0),), Value::Number(1.0));
assert_eq!(fun(op, Value::Number(4.0), Value::Number(2.0),), Value::Number(1.0));
assert_eq!(fun(op, Value::Number(2.0), Value::Number(4.0),), Value::Number(0.0));
assert_eq!(fun(op, Value::Number(4096.0), Value::Number(4096.0)), Value::Number(0.0));
assert_eq!(fun(op, Value::Number(4096.0), Value::Number(4096.0)), Value::Number(4096.0));
assert_eq!(fun(op, Value::Number(4096.0), Value::Number(1024.0)), Value::Number(4096.0));
assert_eq!(fun(op, Value::Number(4096.0), Value::Number(33.0)), Value::Number(2048.0));
assert_eq!(fun(op, Value::Number(4.0), Value::Number(49.0)), Value::Number(0.0));
assert_eq!(fun(op, Value::Number(-5.0), Value::Number(2.0)), Value::Number(1_073_741_822.0));

// "%",
let op = BinaryOperator::Remainder;
Expand Down
Loading