source: frontend/node_modules/webidl-conversions/lib/index.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 14.2 KB
Line 
1"use strict";
2
3function makeException(ErrorType, message, opts = {}) {
4 if (opts.globals) {
5 ErrorType = opts.globals[ErrorType.name];
6 }
7 return new ErrorType(`${opts.context ? opts.context : "Value"} ${message}.`);
8}
9
10function toNumber(value, opts = {}) {
11 if (!opts.globals) {
12 return +value;
13 }
14 if (typeof value === "bigint") {
15 throw opts.globals.TypeError("Cannot convert a BigInt value to a number");
16 }
17 return opts.globals.Number(value);
18}
19
20function type(V) {
21 if (V === null) {
22 return "Null";
23 }
24 switch (typeof V) {
25 case "undefined":
26 return "Undefined";
27 case "boolean":
28 return "Boolean";
29 case "number":
30 return "Number";
31 case "string":
32 return "String";
33 case "symbol":
34 return "Symbol";
35 case "bigint":
36 return "BigInt";
37 case "object":
38 // Falls through
39 case "function":
40 // Falls through
41 default:
42 // Per ES spec, typeof returns an implemention-defined value that is not any of the existing ones for
43 // uncallable non-standard exotic objects. Yet Type() which the Web IDL spec depends on returns Object for
44 // such cases. So treat the default case as an object.
45 return "Object";
46 }
47}
48
49// Round x to the nearest integer, choosing the even integer if it lies halfway between two.
50function evenRound(x) {
51 // There are four cases for numbers with fractional part being .5:
52 //
53 // case | x | floor(x) | round(x) | expected | x <> 0 | x % 1 | x & 1 | example
54 // 1 | 2n + 0.5 | 2n | 2n + 1 | 2n | > | 0.5 | 0 | 0.5 -> 0
55 // 2 | 2n + 1.5 | 2n + 1 | 2n + 2 | 2n + 2 | > | 0.5 | 1 | 1.5 -> 2
56 // 3 | -2n - 0.5 | -2n - 1 | -2n | -2n | < | -0.5 | 0 | -0.5 -> 0
57 // 4 | -2n - 1.5 | -2n - 2 | -2n - 1 | -2n - 2 | < | -0.5 | 1 | -1.5 -> -2
58 // (where n is a non-negative integer)
59 //
60 // Branch here for cases 1 and 4
61 if ((x > 0 && (x % 1) === +0.5 && (x & 1) === 0) ||
62 (x < 0 && (x % 1) === -0.5 && (x & 1) === 1)) {
63 return censorNegativeZero(Math.floor(x));
64 }
65
66 return censorNegativeZero(Math.round(x));
67}
68
69function integerPart(n) {
70 return censorNegativeZero(Math.trunc(n));
71}
72
73function sign(x) {
74 return x < 0 ? -1 : 1;
75}
76
77function modulo(x, y) {
78 // https://tc39.github.io/ecma262/#eqn-modulo
79 // Note that http://stackoverflow.com/a/4467559/3191 does NOT work for large modulos
80 const signMightNotMatch = x % y;
81 if (sign(y) !== sign(signMightNotMatch)) {
82 return signMightNotMatch + y;
83 }
84 return signMightNotMatch;
85}
86
87function censorNegativeZero(x) {
88 return x === 0 ? 0 : x;
89}
90
91function createIntegerConversion(bitLength, typeOpts) {
92 const isSigned = !typeOpts.unsigned;
93
94 let lowerBound;
95 let upperBound;
96 if (bitLength === 64) {
97 upperBound = Number.MAX_SAFE_INTEGER;
98 lowerBound = !isSigned ? 0 : Number.MIN_SAFE_INTEGER;
99 } else if (!isSigned) {
100 lowerBound = 0;
101 upperBound = Math.pow(2, bitLength) - 1;
102 } else {
103 lowerBound = -Math.pow(2, bitLength - 1);
104 upperBound = Math.pow(2, bitLength - 1) - 1;
105 }
106
107 const twoToTheBitLength = Math.pow(2, bitLength);
108 const twoToOneLessThanTheBitLength = Math.pow(2, bitLength - 1);
109
110 return (V, opts = {}) => {
111 let x = toNumber(V, opts);
112 x = censorNegativeZero(x);
113
114 if (opts.enforceRange) {
115 if (!Number.isFinite(x)) {
116 throw makeException(TypeError, "is not a finite number", opts);
117 }
118
119 x = integerPart(x);
120
121 if (x < lowerBound || x > upperBound) {
122 throw makeException(TypeError,
123 `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`, opts);
124 }
125
126 return x;
127 }
128
129 if (!Number.isNaN(x) && opts.clamp) {
130 x = Math.min(Math.max(x, lowerBound), upperBound);
131 x = evenRound(x);
132 return x;
133 }
134
135 if (!Number.isFinite(x) || x === 0) {
136 return 0;
137 }
138 x = integerPart(x);
139
140 // Math.pow(2, 64) is not accurately representable in JavaScript, so try to avoid these per-spec operations if
141 // possible. Hopefully it's an optimization for the non-64-bitLength cases too.
142 if (x >= lowerBound && x <= upperBound) {
143 return x;
144 }
145
146 // These will not work great for bitLength of 64, but oh well. See the README for more details.
147 x = modulo(x, twoToTheBitLength);
148 if (isSigned && x >= twoToOneLessThanTheBitLength) {
149 return x - twoToTheBitLength;
150 }
151 return x;
152 };
153}
154
155function createLongLongConversion(bitLength, { unsigned }) {
156 const upperBound = Number.MAX_SAFE_INTEGER;
157 const lowerBound = unsigned ? 0 : Number.MIN_SAFE_INTEGER;
158 const asBigIntN = unsigned ? BigInt.asUintN : BigInt.asIntN;
159
160 return (V, opts = {}) => {
161 if (opts === undefined) {
162 opts = {};
163 }
164
165 let x = toNumber(V, opts);
166 x = censorNegativeZero(x);
167
168 if (opts.enforceRange) {
169 if (!Number.isFinite(x)) {
170 throw makeException(TypeError, "is not a finite number", opts);
171 }
172
173 x = integerPart(x);
174
175 if (x < lowerBound || x > upperBound) {
176 throw makeException(TypeError,
177 `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`, opts);
178 }
179
180 return x;
181 }
182
183 if (!Number.isNaN(x) && opts.clamp) {
184 x = Math.min(Math.max(x, lowerBound), upperBound);
185 x = evenRound(x);
186 return x;
187 }
188
189 if (!Number.isFinite(x) || x === 0) {
190 return 0;
191 }
192
193 let xBigInt = BigInt(integerPart(x));
194 xBigInt = asBigIntN(bitLength, xBigInt);
195 return Number(xBigInt);
196 };
197}
198
199exports.any = V => {
200 return V;
201};
202
203exports.void = function () {
204 return undefined;
205};
206
207exports.boolean = function (val) {
208 return !!val;
209};
210
211exports.byte = createIntegerConversion(8, { unsigned: false });
212exports.octet = createIntegerConversion(8, { unsigned: true });
213
214exports.short = createIntegerConversion(16, { unsigned: false });
215exports["unsigned short"] = createIntegerConversion(16, { unsigned: true });
216
217exports.long = createIntegerConversion(32, { unsigned: false });
218exports["unsigned long"] = createIntegerConversion(32, { unsigned: true });
219
220exports["long long"] = createLongLongConversion(64, { unsigned: false });
221exports["unsigned long long"] = createLongLongConversion(64, { unsigned: true });
222
223exports.double = (V, opts) => {
224 const x = toNumber(V, opts);
225
226 if (!Number.isFinite(x)) {
227 throw makeException(TypeError, "is not a finite floating-point value", opts);
228 }
229
230 return x;
231};
232
233exports["unrestricted double"] = (V, opts) => {
234 const x = toNumber(V, opts);
235
236 return x;
237};
238
239exports.float = (V, opts) => {
240 const x = toNumber(V, opts);
241
242 if (!Number.isFinite(x)) {
243 throw makeException(TypeError, "is not a finite floating-point value", opts);
244 }
245
246 if (Object.is(x, -0)) {
247 return x;
248 }
249
250 const y = Math.fround(x);
251
252 if (!Number.isFinite(y)) {
253 throw makeException(TypeError, "is outside the range of a single-precision floating-point value", opts);
254 }
255
256 return y;
257};
258
259exports["unrestricted float"] = (V, opts) => {
260 const x = toNumber(V, opts);
261
262 if (isNaN(x)) {
263 return x;
264 }
265
266 if (Object.is(x, -0)) {
267 return x;
268 }
269
270 return Math.fround(x);
271};
272
273exports.DOMString = function (V, opts = {}) {
274 if (opts.treatNullAsEmptyString && V === null) {
275 return "";
276 }
277
278 if (typeof V === "symbol") {
279 throw makeException(TypeError, "is a symbol, which cannot be converted to a string", opts);
280 }
281
282 const StringCtor = opts.globals ? opts.globals.String : String;
283 return StringCtor(V);
284};
285
286exports.ByteString = (V, opts) => {
287 const x = exports.DOMString(V, opts);
288 let c;
289 for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) {
290 if (c > 255) {
291 throw makeException(TypeError, "is not a valid ByteString", opts);
292 }
293 }
294
295 return x;
296};
297
298exports.USVString = (V, opts) => {
299 const S = exports.DOMString(V, opts);
300 const n = S.length;
301 const U = [];
302 for (let i = 0; i < n; ++i) {
303 const c = S.charCodeAt(i);
304 if (c < 0xD800 || c > 0xDFFF) {
305 U.push(String.fromCodePoint(c));
306 } else if (0xDC00 <= c && c <= 0xDFFF) {
307 U.push(String.fromCodePoint(0xFFFD));
308 } else if (i === n - 1) {
309 U.push(String.fromCodePoint(0xFFFD));
310 } else {
311 const d = S.charCodeAt(i + 1);
312 if (0xDC00 <= d && d <= 0xDFFF) {
313 const a = c & 0x3FF;
314 const b = d & 0x3FF;
315 U.push(String.fromCodePoint((2 << 15) + ((2 << 9) * a) + b));
316 ++i;
317 } else {
318 U.push(String.fromCodePoint(0xFFFD));
319 }
320 }
321 }
322
323 return U.join("");
324};
325
326exports.object = (V, opts) => {
327 if (type(V) !== "Object") {
328 throw makeException(TypeError, "is not an object", opts);
329 }
330
331 return V;
332};
333
334// Not exported, but used in Function and VoidFunction.
335
336// Neither Function nor VoidFunction is defined with [TreatNonObjectAsNull], so
337// handling for that is omitted.
338function convertCallbackFunction(V, opts) {
339 if (typeof V !== "function") {
340 throw makeException(TypeError, "is not a function", opts);
341 }
342 return V;
343}
344
345const abByteLengthGetter =
346 Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
347const sabByteLengthGetter =
348 Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, "byteLength").get;
349
350function isNonSharedArrayBuffer(V) {
351 try {
352 // This will throw on SharedArrayBuffers, but not detached ArrayBuffers.
353 // (The spec says it should throw, but the spec conflicts with implementations: https://github.com/tc39/ecma262/issues/678)
354 abByteLengthGetter.call(V);
355
356 return true;
357 } catch {
358 return false;
359 }
360}
361
362function isSharedArrayBuffer(V) {
363 try {
364 sabByteLengthGetter.call(V);
365 return true;
366 } catch {
367 return false;
368 }
369}
370
371function isArrayBufferDetached(V) {
372 try {
373 // eslint-disable-next-line no-new
374 new Uint8Array(V);
375 return false;
376 } catch {
377 return true;
378 }
379}
380
381exports.ArrayBuffer = (V, opts = {}) => {
382 if (!isNonSharedArrayBuffer(V)) {
383 if (opts.allowShared && !isSharedArrayBuffer(V)) {
384 throw makeException(TypeError, "is not an ArrayBuffer or SharedArrayBuffer", opts);
385 }
386 throw makeException(TypeError, "is not an ArrayBuffer", opts);
387 }
388 if (isArrayBufferDetached(V)) {
389 throw makeException(TypeError, "is a detached ArrayBuffer", opts);
390 }
391
392 return V;
393};
394
395const dvByteLengthGetter =
396 Object.getOwnPropertyDescriptor(DataView.prototype, "byteLength").get;
397exports.DataView = (V, opts = {}) => {
398 try {
399 dvByteLengthGetter.call(V);
400 } catch (e) {
401 throw makeException(TypeError, "is not a DataView", opts);
402 }
403
404 if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
405 throw makeException(TypeError, "is backed by a SharedArrayBuffer, which is not allowed", opts);
406 }
407 if (isArrayBufferDetached(V.buffer)) {
408 throw makeException(TypeError, "is backed by a detached ArrayBuffer", opts);
409 }
410
411 return V;
412};
413
414// Returns the unforgeable `TypedArray` constructor name or `undefined`,
415// if the `this` value isn't a valid `TypedArray` object.
416//
417// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag
418const typedArrayNameGetter = Object.getOwnPropertyDescriptor(
419 Object.getPrototypeOf(Uint8Array).prototype,
420 Symbol.toStringTag
421).get;
422[
423 Int8Array, Int16Array, Int32Array, Uint8Array,
424 Uint16Array, Uint32Array, Uint8ClampedArray, Float32Array, Float64Array
425].forEach(func => {
426 const name = func.name;
427 const article = /^[AEIOU]/.test(name) ? "an" : "a";
428 exports[name] = (V, opts = {}) => {
429 if (!ArrayBuffer.isView(V) || typedArrayNameGetter.call(V) !== name) {
430 throw makeException(TypeError, `is not ${article} ${name} object`, opts);
431 }
432 if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
433 throw makeException(TypeError, "is a view on a SharedArrayBuffer, which is not allowed", opts);
434 }
435 if (isArrayBufferDetached(V.buffer)) {
436 throw makeException(TypeError, "is a view on a detached ArrayBuffer", opts);
437 }
438
439 return V;
440 };
441});
442
443// Common definitions
444
445exports.ArrayBufferView = (V, opts = {}) => {
446 if (!ArrayBuffer.isView(V)) {
447 throw makeException(TypeError, "is not a view on an ArrayBuffer or SharedArrayBuffer", opts);
448 }
449
450 if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
451 throw makeException(TypeError, "is a view on a SharedArrayBuffer, which is not allowed", opts);
452 }
453
454 if (isArrayBufferDetached(V.buffer)) {
455 throw makeException(TypeError, "is a view on a detached ArrayBuffer", opts);
456 }
457 return V;
458};
459
460exports.BufferSource = (V, opts = {}) => {
461 if (ArrayBuffer.isView(V)) {
462 if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
463 throw makeException(TypeError, "is a view on a SharedArrayBuffer, which is not allowed", opts);
464 }
465
466 if (isArrayBufferDetached(V.buffer)) {
467 throw makeException(TypeError, "is a view on a detached ArrayBuffer", opts);
468 }
469 return V;
470 }
471
472 if (!opts.allowShared && !isNonSharedArrayBuffer(V)) {
473 throw makeException(TypeError, "is not an ArrayBuffer or a view on one", opts);
474 }
475 if (opts.allowShared && !isSharedArrayBuffer(V) && !isNonSharedArrayBuffer(V)) {
476 throw makeException(TypeError, "is not an ArrayBuffer, SharedArrayBufer, or a view on one", opts);
477 }
478 if (isArrayBufferDetached(V)) {
479 throw makeException(TypeError, "is a detached ArrayBuffer", opts);
480 }
481
482 return V;
483};
484
485exports.DOMTimeStamp = exports["unsigned long long"];
486
487exports.Function = convertCallbackFunction;
488
489exports.VoidFunction = convertCallbackFunction;
Note: See TracBrowser for help on using the repository browser.