-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf_hooks: convert maxSize to IDL value in setResourceTimingBufferSize
ECMAScript values of WebIDL interface parameters should be converted to IDL representatives before the actual implementation, as defined in step 11.5 of the WebIDL Overload resolution algorithm. Refs: https://webidl.spec.whatwg.org/#dfn-create-operation-function Refs: https://webidl.spec.whatwg.org/#es-overloads PR-URL: #44902 Reviewed-By: James M Snell <[email protected]>
- Loading branch information
1 parent
15f1051
commit 7cdf745
Showing
5 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
'use strict'; | ||
|
||
const { | ||
MathAbs, | ||
MathMax, | ||
MathMin, | ||
MathPow, | ||
MathSign, | ||
MathTrunc, | ||
NumberIsNaN, | ||
NumberMAX_SAFE_INTEGER, | ||
NumberMIN_SAFE_INTEGER, | ||
} = primordials; | ||
|
||
const { | ||
codes: { | ||
ERR_INVALID_ARG_VALUE, | ||
}, | ||
} = require('internal/errors'); | ||
const { kEmptyObject } = require('internal/util'); | ||
|
||
// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart | ||
const integerPart = MathTrunc; | ||
|
||
/* eslint-disable node-core/non-ascii-character */ | ||
// Round x to the nearest integer, choosing the even integer if it lies halfway | ||
// between two, and choosing +0 rather than -0. | ||
// This is different from Math.round, which rounds to the next integer in the | ||
// direction of +∞ when the fraction portion is exactly 0.5. | ||
/* eslint-enable node-core/non-ascii-character */ | ||
function evenRound(x) { | ||
// Convert -0 to +0. | ||
const i = integerPart(x) + 0; | ||
const reminder = MathAbs(x % 1); | ||
const sign = MathSign(i); | ||
if (reminder === 0.5) { | ||
return i % 2 === 0 ? i : i + sign; | ||
} | ||
const r = reminder < 0.5 ? i : i + sign; | ||
// Convert -0 to +0. | ||
if (r === 0) { | ||
return 0; | ||
} | ||
return r; | ||
} | ||
|
||
function pow2(exponent) { | ||
// << operates on 32 bit signed integers. | ||
if (exponent < 31) { | ||
return 1 << exponent; | ||
} | ||
if (exponent === 31) { | ||
return 0x8000_0000; | ||
} | ||
if (exponent === 32) { | ||
return 0x1_0000_0000; | ||
} | ||
return MathPow(2, exponent); | ||
} | ||
|
||
// https://tc39.es/ecma262/#eqn-modulo | ||
// The notation “x modulo y” computes a value k of the same sign as y. | ||
function modulo(x, y) { | ||
const r = x % y; | ||
// Convert -0 to +0. | ||
if (r === 0) { | ||
return 0; | ||
} | ||
return r; | ||
} | ||
|
||
// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint | ||
function convertToInt(name, value, bitLength, options = kEmptyObject) { | ||
const { signed = false, enforceRange = false, clamp = false } = options; | ||
|
||
let upperBound; | ||
let lowerBound; | ||
// 1. If bitLength is 64, then: | ||
if (bitLength === 64) { | ||
// 1.1. Let upperBound be 2^53 − 1. | ||
upperBound = NumberMAX_SAFE_INTEGER; | ||
// 1.2. If signedness is "unsigned", then let lowerBound be 0. | ||
// 1.3. Otherwise let lowerBound be −2^53 + 1. | ||
lowerBound = !signed ? 0 : NumberMIN_SAFE_INTEGER; | ||
} else if (!signed) { | ||
// 2. Otherwise, if signedness is "unsigned", then: | ||
// 2.1. Let lowerBound be 0. | ||
// 2.2. Let upperBound be 2^bitLength − 1. | ||
lowerBound = 0; | ||
upperBound = pow2(bitLength) - 1; | ||
} else { | ||
// 3. Otherwise: | ||
// 3.1. Let lowerBound be -2^(bitLength − 1). | ||
// 3.2. Let upperBound be 2^(bitLength − 1) − 1. | ||
lowerBound = -pow2(bitLength - 1); | ||
upperBound = pow2(bitLength - 1) - 1; | ||
} | ||
|
||
// 4. Let x be ? ToNumber(V). | ||
let x = +value; | ||
// 5. If x is −0, then set x to +0. | ||
if (x === 0) { | ||
x = 0; | ||
} | ||
|
||
// 6. If the conversion is to an IDL type associated with the [EnforceRange] | ||
// extended attribute, then: | ||
if (enforceRange) { | ||
// 6.1. If x is NaN, +∞, or −∞, then throw a TypeError. | ||
if (NumberIsNaN(x) || x === Infinity || x === -Infinity) { | ||
throw new ERR_INVALID_ARG_VALUE(name, x); | ||
} | ||
// 6.2. Set x to IntegerPart(x). | ||
x = integerPart(x); | ||
|
||
// 6.3. If x < lowerBound or x > upperBound, then throw a TypeError. | ||
if (x < lowerBound || x > upperBound) { | ||
throw new ERR_INVALID_ARG_VALUE(name, x); | ||
} | ||
|
||
// 6.4. Return x. | ||
return x; | ||
} | ||
|
||
// 7. If x is not NaN and the conversion is to an IDL type associated with | ||
// the [Clamp] extended attribute, then: | ||
if (clamp && !NumberIsNaN(x)) { | ||
// 7.1. Set x to min(max(x, lowerBound), upperBound). | ||
x = MathMin(MathMax(x, lowerBound), upperBound); | ||
|
||
// 7.2. Round x to the nearest integer, choosing the even integer if it | ||
// lies halfway between two, and choosing +0 rather than −0. | ||
x = evenRound(x); | ||
|
||
// 7.3. Return x. | ||
return x; | ||
} | ||
|
||
// 8. If x is NaN, +0, +∞, or −∞, then return +0. | ||
if (NumberIsNaN(x) || x === 0 || x === Infinity || x === -Infinity) { | ||
return 0; | ||
} | ||
|
||
// 9. Set x to IntegerPart(x). | ||
x = integerPart(x); | ||
|
||
// 10. Set x to x modulo 2^bitLength. | ||
x = modulo(x, pow2(bitLength)); | ||
|
||
// 11. If signedness is "signed" and x ≥ 2^(bitLength − 1), then return x − | ||
// 2^bitLength. | ||
if (signed && x >= pow2(bitLength - 1)) { | ||
return x - pow2(bitLength); | ||
} | ||
|
||
// 12. Otherwise, return x. | ||
return x; | ||
} | ||
|
||
module.exports = { | ||
convertToInt, | ||
evenRound, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Flags: --expose-internals | ||
'use strict'; | ||
|
||
require('../common'); | ||
const assert = require('assert'); | ||
const { convertToInt, evenRound } = require('internal/webidl'); | ||
|
||
assert.strictEqual(evenRound(-0.5), 0); | ||
assert.strictEqual(evenRound(0.5), 0); | ||
assert.strictEqual(evenRound(-1.5), -2); | ||
assert.strictEqual(evenRound(1.5), 2); | ||
assert.strictEqual(evenRound(3.4), 3); | ||
assert.strictEqual(evenRound(4.6), 5); | ||
assert.strictEqual(evenRound(5), 5); | ||
assert.strictEqual(evenRound(6), 6); | ||
|
||
// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint | ||
assert.strictEqual(convertToInt('x', 0, 64), 0); | ||
assert.strictEqual(convertToInt('x', 1, 64), 1); | ||
assert.strictEqual(convertToInt('x', -0.5, 64), 0); | ||
assert.strictEqual(convertToInt('x', -0.5, 64, { signed: true }), 0); | ||
assert.strictEqual(convertToInt('x', -1.5, 64, { signed: true }), -1); | ||
|
||
// EnforceRange | ||
const OutOfRangeValues = [ NaN, Infinity, -Infinity, 2 ** 53, -(2 ** 53) ]; | ||
for (const value of OutOfRangeValues) { | ||
assert.throws(() => convertToInt('x', value, 64, { enforceRange: true }), { | ||
name: 'TypeError', | ||
code: 'ERR_INVALID_ARG_VALUE', | ||
}); | ||
} | ||
|
||
// Out of range: clamp | ||
assert.strictEqual(convertToInt('x', NaN, 64, { clamp: true }), 0); | ||
assert.strictEqual(convertToInt('x', Infinity, 64, { clamp: true }), Number.MAX_SAFE_INTEGER); | ||
assert.strictEqual(convertToInt('x', -Infinity, 64, { clamp: true }), 0); | ||
assert.strictEqual(convertToInt('x', -Infinity, 64, { signed: true, clamp: true }), Number.MIN_SAFE_INTEGER); | ||
assert.strictEqual(convertToInt('x', 0x1_0000_0000, 32, { clamp: true }), 0xFFFF_FFFF); | ||
assert.strictEqual(convertToInt('x', 0xFFFF_FFFF, 32, { clamp: true }), 0xFFFF_FFFF); | ||
assert.strictEqual(convertToInt('x', 0x8000_0000, 32, { clamp: true, signed: true }), 0x7FFF_FFFF); | ||
assert.strictEqual(convertToInt('x', 0xFFFF_FFFF, 32, { clamp: true, signed: true }), 0x7FFF_FFFF); | ||
assert.strictEqual(convertToInt('x', 0.5, 64, { clamp: true }), 0); | ||
assert.strictEqual(convertToInt('x', 1.5, 64, { clamp: true }), 2); | ||
assert.strictEqual(convertToInt('x', -0.5, 64, { clamp: true }), 0); | ||
assert.strictEqual(convertToInt('x', -0.5, 64, { signed: true, clamp: true }), 0); | ||
assert.strictEqual(convertToInt('x', -1.5, 64, { signed: true, clamp: true }), -2); | ||
|
||
// Out of range, step 8. | ||
assert.strictEqual(convertToInt('x', NaN, 64), 0); | ||
assert.strictEqual(convertToInt('x', Infinity, 64), 0); | ||
assert.strictEqual(convertToInt('x', -Infinity, 64), 0); | ||
assert.strictEqual(convertToInt('x', 0x1_0000_0000, 32), 0); | ||
assert.strictEqual(convertToInt('x', 0x1_0000_0001, 32), 1); | ||
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters