index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. "use strict";
  2. function makeException(ErrorType, message, options) {
  3. if (options.globals) {
  4. ErrorType = options.globals[ErrorType.name];
  5. }
  6. return new ErrorType(`${options.context ? options.context : "Value"} ${message}.`);
  7. }
  8. function toNumber(value, options) {
  9. if (typeof value === "bigint") {
  10. throw makeException(TypeError, "is a BigInt which cannot be converted to a number", options);
  11. }
  12. if (!options.globals) {
  13. return Number(value);
  14. }
  15. return options.globals.Number(value);
  16. }
  17. // Round x to the nearest integer, choosing the even integer if it lies halfway between two.
  18. function evenRound(x) {
  19. // There are four cases for numbers with fractional part being .5:
  20. //
  21. // case | x | floor(x) | round(x) | expected | x <> 0 | x % 1 | x & 1 | example
  22. // 1 | 2n + 0.5 | 2n | 2n + 1 | 2n | > | 0.5 | 0 | 0.5 -> 0
  23. // 2 | 2n + 1.5 | 2n + 1 | 2n + 2 | 2n + 2 | > | 0.5 | 1 | 1.5 -> 2
  24. // 3 | -2n - 0.5 | -2n - 1 | -2n | -2n | < | -0.5 | 0 | -0.5 -> 0
  25. // 4 | -2n - 1.5 | -2n - 2 | -2n - 1 | -2n - 2 | < | -0.5 | 1 | -1.5 -> -2
  26. // (where n is a non-negative integer)
  27. //
  28. // Branch here for cases 1 and 4
  29. if ((x > 0 && (x % 1) === +0.5 && (x & 1) === 0) ||
  30. (x < 0 && (x % 1) === -0.5 && (x & 1) === 1)) {
  31. return censorNegativeZero(Math.floor(x));
  32. }
  33. return censorNegativeZero(Math.round(x));
  34. }
  35. function integerPart(n) {
  36. return censorNegativeZero(Math.trunc(n));
  37. }
  38. function sign(x) {
  39. return x < 0 ? -1 : 1;
  40. }
  41. function modulo(x, y) {
  42. // https://tc39.github.io/ecma262/#eqn-modulo
  43. // Note that http://stackoverflow.com/a/4467559/3191 does NOT work for large modulos
  44. const signMightNotMatch = x % y;
  45. if (sign(y) !== sign(signMightNotMatch)) {
  46. return signMightNotMatch + y;
  47. }
  48. return signMightNotMatch;
  49. }
  50. function censorNegativeZero(x) {
  51. return x === 0 ? 0 : x;
  52. }
  53. function createIntegerConversion(bitLength, { unsigned }) {
  54. let lowerBound, upperBound;
  55. if (unsigned) {
  56. lowerBound = 0;
  57. upperBound = 2 ** bitLength - 1;
  58. } else {
  59. lowerBound = -(2 ** (bitLength - 1));
  60. upperBound = 2 ** (bitLength - 1) - 1;
  61. }
  62. const twoToTheBitLength = 2 ** bitLength;
  63. const twoToOneLessThanTheBitLength = 2 ** (bitLength - 1);
  64. return (value, options = {}) => {
  65. let x = toNumber(value, options);
  66. x = censorNegativeZero(x);
  67. if (options.enforceRange) {
  68. if (!Number.isFinite(x)) {
  69. throw makeException(TypeError, "is not a finite number", options);
  70. }
  71. x = integerPart(x);
  72. if (x < lowerBound || x > upperBound) {
  73. throw makeException(
  74. TypeError,
  75. `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`,
  76. options
  77. );
  78. }
  79. return x;
  80. }
  81. if (!Number.isNaN(x) && options.clamp) {
  82. x = Math.min(Math.max(x, lowerBound), upperBound);
  83. x = evenRound(x);
  84. return x;
  85. }
  86. if (!Number.isFinite(x) || x === 0) {
  87. return 0;
  88. }
  89. x = integerPart(x);
  90. // Math.pow(2, 64) is not accurately representable in JavaScript, so try to avoid these per-spec operations if
  91. // possible. Hopefully it's an optimization for the non-64-bitLength cases too.
  92. if (x >= lowerBound && x <= upperBound) {
  93. return x;
  94. }
  95. // These will not work great for bitLength of 64, but oh well. See the README for more details.
  96. x = modulo(x, twoToTheBitLength);
  97. if (!unsigned && x >= twoToOneLessThanTheBitLength) {
  98. return x - twoToTheBitLength;
  99. }
  100. return x;
  101. };
  102. }
  103. function createLongLongConversion(bitLength, { unsigned }) {
  104. const upperBound = Number.MAX_SAFE_INTEGER;
  105. const lowerBound = unsigned ? 0 : Number.MIN_SAFE_INTEGER;
  106. const asBigIntN = unsigned ? BigInt.asUintN : BigInt.asIntN;
  107. return (value, options = {}) => {
  108. let x = toNumber(value, options);
  109. x = censorNegativeZero(x);
  110. if (options.enforceRange) {
  111. if (!Number.isFinite(x)) {
  112. throw makeException(TypeError, "is not a finite number", options);
  113. }
  114. x = integerPart(x);
  115. if (x < lowerBound || x > upperBound) {
  116. throw makeException(
  117. TypeError,
  118. `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`,
  119. options
  120. );
  121. }
  122. return x;
  123. }
  124. if (!Number.isNaN(x) && options.clamp) {
  125. x = Math.min(Math.max(x, lowerBound), upperBound);
  126. x = evenRound(x);
  127. return x;
  128. }
  129. if (!Number.isFinite(x) || x === 0) {
  130. return 0;
  131. }
  132. let xBigInt = BigInt(integerPart(x));
  133. xBigInt = asBigIntN(bitLength, xBigInt);
  134. return Number(xBigInt);
  135. };
  136. }
  137. exports.any = value => {
  138. return value;
  139. };
  140. exports.undefined = () => {
  141. return undefined;
  142. };
  143. exports.boolean = value => {
  144. return Boolean(value);
  145. };
  146. exports.byte = createIntegerConversion(8, { unsigned: false });
  147. exports.octet = createIntegerConversion(8, { unsigned: true });
  148. exports.short = createIntegerConversion(16, { unsigned: false });
  149. exports["unsigned short"] = createIntegerConversion(16, { unsigned: true });
  150. exports.long = createIntegerConversion(32, { unsigned: false });
  151. exports["unsigned long"] = createIntegerConversion(32, { unsigned: true });
  152. exports["long long"] = createLongLongConversion(64, { unsigned: false });
  153. exports["unsigned long long"] = createLongLongConversion(64, { unsigned: true });
  154. exports.double = (value, options = {}) => {
  155. const x = toNumber(value, options);
  156. if (!Number.isFinite(x)) {
  157. throw makeException(TypeError, "is not a finite floating-point value", options);
  158. }
  159. return x;
  160. };
  161. exports["unrestricted double"] = (value, options = {}) => {
  162. const x = toNumber(value, options);
  163. return x;
  164. };
  165. exports.float = (value, options = {}) => {
  166. const x = toNumber(value, options);
  167. if (!Number.isFinite(x)) {
  168. throw makeException(TypeError, "is not a finite floating-point value", options);
  169. }
  170. if (Object.is(x, -0)) {
  171. return x;
  172. }
  173. const y = Math.fround(x);
  174. if (!Number.isFinite(y)) {
  175. throw makeException(TypeError, "is outside the range of a single-precision floating-point value", options);
  176. }
  177. return y;
  178. };
  179. exports["unrestricted float"] = (value, options = {}) => {
  180. const x = toNumber(value, options);
  181. if (isNaN(x)) {
  182. return x;
  183. }
  184. if (Object.is(x, -0)) {
  185. return x;
  186. }
  187. return Math.fround(x);
  188. };
  189. exports.DOMString = (value, options = {}) => {
  190. if (options.treatNullAsEmptyString && value === null) {
  191. return "";
  192. }
  193. if (typeof value === "symbol") {
  194. throw makeException(TypeError, "is a symbol, which cannot be converted to a string", options);
  195. }
  196. const StringCtor = options.globals ? options.globals.String : String;
  197. return StringCtor(value);
  198. };
  199. exports.ByteString = (value, options = {}) => {
  200. const x = exports.DOMString(value, options);
  201. let c;
  202. for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) {
  203. if (c > 255) {
  204. throw makeException(TypeError, "is not a valid ByteString", options);
  205. }
  206. }
  207. return x;
  208. };
  209. exports.USVString = (value, options = {}) => {
  210. const S = exports.DOMString(value, options);
  211. const n = S.length;
  212. const U = [];
  213. for (let i = 0; i < n; ++i) {
  214. const c = S.charCodeAt(i);
  215. if (c < 0xD800 || c > 0xDFFF) {
  216. U.push(String.fromCodePoint(c));
  217. } else if (0xDC00 <= c && c <= 0xDFFF) {
  218. U.push(String.fromCodePoint(0xFFFD));
  219. } else if (i === n - 1) {
  220. U.push(String.fromCodePoint(0xFFFD));
  221. } else {
  222. const d = S.charCodeAt(i + 1);
  223. if (0xDC00 <= d && d <= 0xDFFF) {
  224. const a = c & 0x3FF;
  225. const b = d & 0x3FF;
  226. U.push(String.fromCodePoint((2 << 15) + ((2 << 9) * a) + b));
  227. ++i;
  228. } else {
  229. U.push(String.fromCodePoint(0xFFFD));
  230. }
  231. }
  232. }
  233. return U.join("");
  234. };
  235. exports.object = (value, options = {}) => {
  236. if (value === null || (typeof value !== "object" && typeof value !== "function")) {
  237. throw makeException(TypeError, "is not an object", options);
  238. }
  239. return value;
  240. };
  241. const abByteLengthGetter =
  242. Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
  243. const sabByteLengthGetter =
  244. typeof SharedArrayBuffer === "function" ?
  245. Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, "byteLength").get :
  246. null;
  247. function isNonSharedArrayBuffer(value) {
  248. try {
  249. // This will throw on SharedArrayBuffers, but not detached ArrayBuffers.
  250. // (The spec says it should throw, but the spec conflicts with implementations: https://github.com/tc39/ecma262/issues/678)
  251. abByteLengthGetter.call(value);
  252. return true;
  253. } catch {
  254. return false;
  255. }
  256. }
  257. function isSharedArrayBuffer(value) {
  258. try {
  259. sabByteLengthGetter.call(value);
  260. return true;
  261. } catch {
  262. return false;
  263. }
  264. }
  265. function isArrayBufferDetached(value) {
  266. try {
  267. // eslint-disable-next-line no-new
  268. new Uint8Array(value);
  269. return false;
  270. } catch {
  271. return true;
  272. }
  273. }
  274. exports.ArrayBuffer = (value, options = {}) => {
  275. if (!isNonSharedArrayBuffer(value)) {
  276. if (options.allowShared && !isSharedArrayBuffer(value)) {
  277. throw makeException(TypeError, "is not an ArrayBuffer or SharedArrayBuffer", options);
  278. }
  279. throw makeException(TypeError, "is not an ArrayBuffer", options);
  280. }
  281. if (isArrayBufferDetached(value)) {
  282. throw makeException(TypeError, "is a detached ArrayBuffer", options);
  283. }
  284. return value;
  285. };
  286. const dvByteLengthGetter =
  287. Object.getOwnPropertyDescriptor(DataView.prototype, "byteLength").get;
  288. exports.DataView = (value, options = {}) => {
  289. try {
  290. dvByteLengthGetter.call(value);
  291. } catch (e) {
  292. throw makeException(TypeError, "is not a DataView", options);
  293. }
  294. if (!options.allowShared && isSharedArrayBuffer(value.buffer)) {
  295. throw makeException(TypeError, "is backed by a SharedArrayBuffer, which is not allowed", options);
  296. }
  297. if (isArrayBufferDetached(value.buffer)) {
  298. throw makeException(TypeError, "is backed by a detached ArrayBuffer", options);
  299. }
  300. return value;
  301. };
  302. // Returns the unforgeable `TypedArray` constructor name or `undefined`,
  303. // if the `this` value isn't a valid `TypedArray` object.
  304. //
  305. // https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag
  306. const typedArrayNameGetter = Object.getOwnPropertyDescriptor(
  307. Object.getPrototypeOf(Uint8Array).prototype,
  308. Symbol.toStringTag
  309. ).get;
  310. [
  311. Int8Array,
  312. Int16Array,
  313. Int32Array,
  314. Uint8Array,
  315. Uint16Array,
  316. Uint32Array,
  317. Uint8ClampedArray,
  318. Float32Array,
  319. Float64Array
  320. ].forEach(func => {
  321. const { name } = func;
  322. const article = /^[AEIOU]/u.test(name) ? "an" : "a";
  323. exports[name] = (value, options = {}) => {
  324. if (!ArrayBuffer.isView(value) || typedArrayNameGetter.call(value) !== name) {
  325. throw makeException(TypeError, `is not ${article} ${name} object`, options);
  326. }
  327. if (!options.allowShared && isSharedArrayBuffer(value.buffer)) {
  328. throw makeException(TypeError, "is a view on a SharedArrayBuffer, which is not allowed", options);
  329. }
  330. if (isArrayBufferDetached(value.buffer)) {
  331. throw makeException(TypeError, "is a view on a detached ArrayBuffer", options);
  332. }
  333. return value;
  334. };
  335. });
  336. // Common definitions
  337. exports.ArrayBufferView = (value, options = {}) => {
  338. if (!ArrayBuffer.isView(value)) {
  339. throw makeException(TypeError, "is not a view on an ArrayBuffer or SharedArrayBuffer", options);
  340. }
  341. if (!options.allowShared && isSharedArrayBuffer(value.buffer)) {
  342. throw makeException(TypeError, "is a view on a SharedArrayBuffer, which is not allowed", options);
  343. }
  344. if (isArrayBufferDetached(value.buffer)) {
  345. throw makeException(TypeError, "is a view on a detached ArrayBuffer", options);
  346. }
  347. return value;
  348. };
  349. exports.BufferSource = (value, options = {}) => {
  350. if (ArrayBuffer.isView(value)) {
  351. if (!options.allowShared && isSharedArrayBuffer(value.buffer)) {
  352. throw makeException(TypeError, "is a view on a SharedArrayBuffer, which is not allowed", options);
  353. }
  354. if (isArrayBufferDetached(value.buffer)) {
  355. throw makeException(TypeError, "is a view on a detached ArrayBuffer", options);
  356. }
  357. return value;
  358. }
  359. if (!options.allowShared && !isNonSharedArrayBuffer(value)) {
  360. throw makeException(TypeError, "is not an ArrayBuffer or a view on one", options);
  361. }
  362. if (options.allowShared && !isSharedArrayBuffer(value) && !isNonSharedArrayBuffer(value)) {
  363. throw makeException(TypeError, "is not an ArrayBuffer, SharedArrayBuffer, or a view on one", options);
  364. }
  365. if (isArrayBufferDetached(value)) {
  366. throw makeException(TypeError, "is a detached ArrayBuffer", options);
  367. }
  368. return value;
  369. };
  370. exports.DOMTimeStamp = exports["unsigned long long"];