• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
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
16const {
17  codes: {
18    ERR_INVALID_ARG_VALUE,
19  },
20} = require('internal/errors');
21const { kEmptyObject } = require('internal/util');
22
23const converters = { __proto__: null };
24
25// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
26const 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 */
34function 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
50function 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.
66function 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
76function 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 */
168converters.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
176module.exports = {
177  convertToInt,
178  evenRound,
179  converters,
180};
181