From 0f245d2894e0c491729e488397cc66cf23a727b5 Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Sat, 25 Apr 2026 03:15:13 +0400 Subject: [PATCH] lib: fix modulo sign in webidl convertToInt The modulo() helper in lib/internal/webidl.js is documented to implement ECMA-262's "x modulo y" (result has the same sign as y), but it used the JS % operator directly, whose result takes the sign of x. convertToInt step 10 relies on modulo(x, 2^bitLength) producing a non-negative representative in [0, 2^bitLength); step 11 only handles the signed upper half, so negative results of % were returned unchanged. As a result, negative inputs to unsigned WebIDL integer types returned the original negative value instead of the two's-complement wrap (e.g. convertToInt('v', -3, 8) returned -3 instead of 253), and signed inputs in the one-wrap band below the lower bound were returned as-is (convertToInt('v', -200, 8, { signed: true }) returned -200 instead of 56). This diverged from typed-array coercion. Co-Authored-By: Claude Co-Authored-By: DeepView Autofix <276251120+deepview-autofix@users.noreply.github.com> Co-Authored-By: Nikita Skovoroda Assisted-by: Claude Signed-off-by: Nikita Skovoroda --- lib/internal/webidl.js | 3 +++ test/parallel/test-internal-webidl-converttoint.js | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/internal/webidl.js b/lib/internal/webidl.js index 36bde94013d1a8..85bfb2c3db3180 100644 --- a/lib/internal/webidl.js +++ b/lib/internal/webidl.js @@ -108,11 +108,14 @@ function pow2(exponent) { // https://tc39.es/ecma262/#eqn-modulo // The notation “x modulo y” computes a value k of the same sign as y. +// As y is always positive in this file, we can do a simple r < 0 check for that. function modulo(x, y) { const r = x % y; // Convert -0 to +0. if (r === 0) { return 0; + } else if (r < 0) { + return r + y; } return r; } diff --git a/test/parallel/test-internal-webidl-converttoint.js b/test/parallel/test-internal-webidl-converttoint.js index 7e7c024387a0ec..8260d9d8d29914 100644 --- a/test/parallel/test-internal-webidl-converttoint.js +++ b/test/parallel/test-internal-webidl-converttoint.js @@ -56,3 +56,12 @@ assert.strictEqual(convertToInt('x', 0xFFFF_FFFF, 32), 0xFFFF_FFFF); // Out of range, step 11. assert.strictEqual(convertToInt('x', 0x8000_0000, 32, { signed: true }), -0x8000_0000); assert.strictEqual(convertToInt('x', 0xFFF_FFFF, 32, { signed: true }), 0xFFF_FFFF); + +// Negative values must wrap as two's-complement, matching typed-array behavior. +assert.strictEqual(convertToInt('x', -3, 8), 253); +assert.strictEqual(convertToInt('x', -3, 8), new Uint8Array([-3])[0]); +assert.strictEqual(convertToInt('x', -1, 32), 0xFFFF_FFFF); +assert.strictEqual(convertToInt('x', -1, 32), new Uint32Array([-1])[0]); +assert.strictEqual(convertToInt('x', -200, 8, { signed: true }), 56); +assert.strictEqual(convertToInt('x', -200, 8, { signed: true }), + new Int8Array([-200])[0]);