Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions lib/internal/perf/performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const timerify = require('internal/perf/timerify');
const { customInspectSymbol: kInspect, kEnumerableProperty, kEmptyObject } = require('internal/util');
const { inspect } = require('util');
const { validateInternalField } = require('internal/validators');
const { convertToInt } = require('internal/webidl');

const {
getTimeOriginTimestamp
Expand Down Expand Up @@ -144,6 +145,8 @@ class Performance extends EventTarget {
if (arguments.length === 0) {
throw new ERR_MISSING_ARGS('maxSize');
}
// unsigned long
maxSize = convertToInt('maxSize', maxSize, 32);
return setResourceTimingBufferSize(maxSize);
}

Expand Down
161 changes: 161 additions & 0 deletions lib/internal/webidl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
'use strict';

const {
MathAbs,
MathMax,
MathMin,
MathPow,
MathTrunc,
NumberIsNaN,
NumberMAX_SAFE_INTEGER,
NumberMIN_SAFE_INTEGER,
} = primordials;

const {
codes: {
ERR_INVALID_ARG_VALUE,
},
} = require('internal/errors');
const { kEmptyObject } = require('internal/util');

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 = i < 0 ? -1 : 1;
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 (!NumberIsNaN(x) && clamp) {
// 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,
};
1 change: 1 addition & 0 deletions test/parallel/test-bootstrap-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const expectedModules = new Set([
'NativeModule internal/validators',
'NativeModule internal/vm/module',
'NativeModule internal/wasm_web_api',
'NativeModule internal/webidl',
'NativeModule internal/webstreams/adapters',
'NativeModule internal/webstreams/compression',
'NativeModule internal/webstreams/encoding',
Expand Down
58 changes: 58 additions & 0 deletions test/parallel/test-internal-webidl-converttoint.js
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);
11 changes: 11 additions & 0 deletions test/parallel/test-performance-resourcetimingbuffersize.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ const initiatorType = '';
const cacheMode = '';

async function main() {
// Invalid buffer size values are converted to 0.
const invalidValues = [ null, undefined, true, false, -1, 0.5, Infinity, NaN, '', 'foo', {}, [], () => {} ];
for (const value of invalidValues) {
performance.setResourceTimingBufferSize(value);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode);
assert.strictEqual(performance.getEntriesByType('resource').length, 0);
performance.clearResourceTimings();
}
// Wait for the buffer full event to be cleared.
await waitBufferFullEvent();

performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode);
// Trigger a resourcetimingbufferfull event.
Expand Down