mirror of
https://github.com/react/react.git
synced 2026-09-29 05:33:03 +08:00
## Summary The Rust port's constant propagation folds `**` with `f64::powf`, which follows IEEE 754. IEEE `pow` returns `1` for `pow(1, NaN)` and `pow(±1, ±∞)`, but ECMAScript's [`Number::exponentiate`](https://tc39.es/ecma262/#sec-numeric-types-number-exponentiate) returns `NaN` in those cases. The TS compiler folds with native `lhs ** rhs`, so the two backends disagree, and the Rust-compiled output changes runtime behavior: ```js const x = 1 ** (1 / 0); // JS: NaN, Rust-compiled: 1 ``` This adds `js_exponentiate`, which returns `NaN` when `|base| == 1` and the exponent is not finite, and otherwise defers to `powf`. `**=` lowers to the same `BinaryOperator::Exponent`, so it is covered too. To check the guard is neither too narrow nor too broad, I compared `f64::powf` against JS `**` over all 225 pairs drawn from {NaN, ±Infinity, ±0, ±1, ±2, ±0.5, ±3, ±1e308}. The only mismatches were `1 ** NaN`, `1 ** ±Infinity` and `(-1) ** ±Infinity`. The condition also matches `(-1) ** NaN`, where `powf` already returns `NaN`. While in this file I noticed two places where `js_to_number` diverges from `ToNumber`. I left them out to keep this PR focused, and can send a follow-up if that's useful: - `trimmed.parse::<f64>()` accepts `"inf"`, `"infinity"` and `"INFINITY"`, so `"inf" == 1 / 0` folds to `true` (JS: `false`). - The `0x`/`0o`/`0b` branches use `u64::from_str_radix`, which fails past 64 bits (`"0xFFFFFFFFFFFFFFFFF" == 295147905179352830000` folds to `false`, JS: `true`) and accepts a sign after the prefix (`"0x+10" == 16` folds to `true`, JS: `false`). ## How did you test this change? Added `constant-propagation-exponent-non-finite.js` with the five mismatching cases plus five controls: `(-1) ** NaN`, `NaN ** 0`, `2 ** Infinity`, `0.5 ** Infinity` and `2 ** 10`. The snapshot was generated with the TS compiler. - Before the fix, `yarn snap --rust -p constant-propagation-exponent-non-finite` fails on eval output: expected `[null,null,null,null,null,null,1,null,0,1024]`, got `[1,1,1,1,1,null,1,null,0,1024]`. - With a narrower guard (`exponent.is_infinite()` instead of `!exponent.is_finite()`) it still fails, only on `1 ** NaN`. - With the fix, it passes on both backends. I also ran the steps from `compiler_rust.yml` locally: - `cargo check`, `cargo build`, `cargo test --workspace` (49 passed) - `bash scripts/test-babel-ast.sh` - `bash scripts/test-rust-port.sh`: 1816 passed, 0 failed (1815 on `main`, before the new fixture) - `yarn snap --rust` and `yarn snap`: 1817 passed, 0 failed