Skip to content

Commit f403c2d

Browse files
KhafraDevdanielleadams
authored andcommitted
lib: use webidl DOMString converter in EventTarget
PR-URL: #47514 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent ebc8f38 commit f403c2d

File tree

4 files changed

+196
-2
lines changed

4 files changed

+196
-2
lines changed

lib/internal/event_target.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const {
4242
kEnumerableProperty,
4343
} = require('internal/util');
4444
const { inspect } = require('util');
45+
const webidl = require('internal/webidl');
4546

4647
const kIsEventTarget = SymbolFor('nodejs.event_target');
4748
const kIsNodeEventTarget = Symbol('kIsNodeEventTarget');
@@ -577,7 +578,7 @@ class EventTarget {
577578
process.emitWarning(w);
578579
return;
579580
}
580-
type = String(type);
581+
type = webidl.converters.DOMString(type);
581582

582583
if (signal) {
583584
if (signal.aborted) {
@@ -643,7 +644,7 @@ class EventTarget {
643644
if (!validateEventListener(listener))
644645
return;
645646

646-
type = String(type);
647+
type = webidl.converters.DOMString(type);
647648
const capture = options?.capture === true;
648649

649650
const root = this[kEvents].get(type);

lib/internal/webidl.js

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
'use strict';
2+
3+
const {
4+
MathAbs,
5+
MathMax,
6+
MathMin,
7+
MathPow,
8+
MathSign,
9+
MathTrunc,
10+
NumberIsNaN,
11+
NumberMAX_SAFE_INTEGER,
12+
NumberMIN_SAFE_INTEGER,
13+
String,
14+
} = primordials;
15+
16+
const {
17+
codes: {
18+
ERR_INVALID_ARG_VALUE,
19+
},
20+
} = require('internal/errors');
21+
const { kEmptyObject } = require('internal/util');
22+
23+
const converters = { __proto__: null };
24+
25+
// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
26+
const integerPart = MathTrunc;
27+
28+
/* eslint-disable node-core/non-ascii-character */
29+
// Round x to the nearest integer, choosing the even integer if it lies halfway
30+
// between two, and choosing +0 rather than -0.
31+
// This is different from Math.round, which rounds to the next integer in the
32+
// direction of +∞ when the fraction portion is exactly 0.5.
33+
/* eslint-enable node-core/non-ascii-character */
34+
function evenRound(x) {
35+
// Convert -0 to +0.
36+
const i = integerPart(x) + 0;
37+
const reminder = MathAbs(x % 1);
38+
const sign = MathSign(i);
39+
if (reminder === 0.5) {
40+
return i % 2 === 0 ? i : i + sign;
41+
}
42+
const r = reminder < 0.5 ? i : i + sign;
43+
// Convert -0 to +0.
44+
if (r === 0) {
45+
return 0;
46+
}
47+
return r;
48+
}
49+
50+
function pow2(exponent) {
51+
// << operates on 32 bit signed integers.
52+
if (exponent < 31) {
53+
return 1 << exponent;
54+
}
55+
if (exponent === 31) {
56+
return 0x8000_0000;
57+
}
58+
if (exponent === 32) {
59+
return 0x1_0000_0000;
60+
}
61+
return MathPow(2, exponent);
62+
}
63+
64+
// https://tc39.es/ecma262/#eqn-modulo
65+
// The notation “x modulo y” computes a value k of the same sign as y.
66+
function modulo(x, y) {
67+
const r = x % y;
68+
// Convert -0 to +0.
69+
if (r === 0) {
70+
return 0;
71+
}
72+
return r;
73+
}
74+
75+
// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
76+
function convertToInt(name, value, bitLength, options = kEmptyObject) {
77+
const { signed = false, enforceRange = false, clamp = false } = options;
78+
79+
let upperBound;
80+
let lowerBound;
81+
// 1. If bitLength is 64, then:
82+
if (bitLength === 64) {
83+
// 1.1. Let upperBound be 2^53 − 1.
84+
upperBound = NumberMAX_SAFE_INTEGER;
85+
// 1.2. If signedness is "unsigned", then let lowerBound be 0.
86+
// 1.3. Otherwise let lowerBound be −2^53 + 1.
87+
lowerBound = !signed ? 0 : NumberMIN_SAFE_INTEGER;
88+
} else if (!signed) {
89+
// 2. Otherwise, if signedness is "unsigned", then:
90+
// 2.1. Let lowerBound be 0.
91+
// 2.2. Let upperBound be 2^bitLength − 1.
92+
lowerBound = 0;
93+
upperBound = pow2(bitLength) - 1;
94+
} else {
95+
// 3. Otherwise:
96+
// 3.1. Let lowerBound be -2^(bitLength − 1).
97+
// 3.2. Let upperBound be 2^(bitLength − 1) − 1.
98+
lowerBound = -pow2(bitLength - 1);
99+
upperBound = pow2(bitLength - 1) - 1;
100+
}
101+
102+
// 4. Let x be ? ToNumber(V).
103+
let x = +value;
104+
// 5. If x is −0, then set x to +0.
105+
if (x === 0) {
106+
x = 0;
107+
}
108+
109+
// 6. If the conversion is to an IDL type associated with the [EnforceRange]
110+
// extended attribute, then:
111+
if (enforceRange) {
112+
// 6.1. If x is NaN, +∞, or −∞, then throw a TypeError.
113+
if (NumberIsNaN(x) || x === Infinity || x === -Infinity) {
114+
throw new ERR_INVALID_ARG_VALUE(name, x);
115+
}
116+
// 6.2. Set x to IntegerPart(x).
117+
x = integerPart(x);
118+
119+
// 6.3. If x < lowerBound or x > upperBound, then throw a TypeError.
120+
if (x < lowerBound || x > upperBound) {
121+
throw new ERR_INVALID_ARG_VALUE(name, x);
122+
}
123+
124+
// 6.4. Return x.
125+
return x;
126+
}
127+
128+
// 7. If x is not NaN and the conversion is to an IDL type associated with
129+
// the [Clamp] extended attribute, then:
130+
if (clamp && !NumberIsNaN(x)) {
131+
// 7.1. Set x to min(max(x, lowerBound), upperBound).
132+
x = MathMin(MathMax(x, lowerBound), upperBound);
133+
134+
// 7.2. Round x to the nearest integer, choosing the even integer if it
135+
// lies halfway between two, and choosing +0 rather than −0.
136+
x = evenRound(x);
137+
138+
// 7.3. Return x.
139+
return x;
140+
}
141+
142+
// 8. If x is NaN, +0, +∞, or −∞, then return +0.
143+
if (NumberIsNaN(x) || x === 0 || x === Infinity || x === -Infinity) {
144+
return 0;
145+
}
146+
147+
// 9. Set x to IntegerPart(x).
148+
x = integerPart(x);
149+
150+
// 10. Set x to x modulo 2^bitLength.
151+
x = modulo(x, pow2(bitLength));
152+
153+
// 11. If signedness is "signed" and x ≥ 2^(bitLength − 1), then return x −
154+
// 2^bitLength.
155+
if (signed && x >= pow2(bitLength - 1)) {
156+
return x - pow2(bitLength);
157+
}
158+
159+
// 12. Otherwise, return x.
160+
return x;
161+
}
162+
163+
/**
164+
* @see https://webidl.spec.whatwg.org/#es-DOMString
165+
* @param {any} V
166+
* @returns {string}
167+
*/
168+
converters.DOMString = function DOMString(V) {
169+
if (typeof V === 'symbol') {
170+
throw new ERR_INVALID_ARG_VALUE('value', V);
171+
}
172+
173+
return String(V);
174+
};
175+
176+
module.exports = {
177+
convertToInt,
178+
evenRound,
179+
converters,
180+
};

test/parallel/test-bootstrap-modules.js

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ const expectedModules = new Set([
151151
'NativeModule internal/vm',
152152
'NativeModule internal/vm/module',
153153
'NativeModule internal/wasm_web_api',
154+
'NativeModule internal/webidl',
154155
'NativeModule internal/webstreams/adapters',
155156
'NativeModule internal/webstreams/compression',
156157
'NativeModule internal/webstreams/encoding',

test/parallel/test-eventtarget.js

+12
Original file line numberDiff line numberDiff line change
@@ -705,3 +705,15 @@ let asyncTest = Promise.resolve();
705705
name: 'TypeError',
706706
});
707707
}
708+
709+
{
710+
const et = new EventTarget();
711+
712+
throws(() => {
713+
et.addEventListener(Symbol('symbol'), () => {});
714+
}, TypeError);
715+
716+
throws(() => {
717+
et.removeEventListener(Symbol('symbol'), () => {});
718+
}, TypeError);
719+
}

0 commit comments

Comments
 (0)