mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
fix(linter/tree-shaking): correct the calculation of >>, << and >>> (#4932)
fixes: #4914
This commit is contained in:
parent
80d0d1f0c4
commit
508644a34e
2 changed files with 51 additions and 18 deletions
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
@ -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,
|
||||
|
|
@ -601,11 +623,18 @@ 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;
|
||||
|
|
@ -613,7 +642,11 @@ 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(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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue