array-callbacks-test262.test.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
  3. * - test/built-ins/Array/prototype/map/15.4.4.19-8-1.js
  4. * - test/built-ins/Array/prototype/map/15.4.4.19-8-2.js
  5. * - test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js
  6. * - test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js
  7. * - test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js
  8. * - test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js
  9. * - test/built-ins/Array/prototype/find/predicate-call-parameters.js
  10. * - test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js
  11. * - test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js
  12. * - test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js
  13. * - test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js
  14. * - test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js
  15. * - test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js
  16. * - test/built-ins/Array/prototype/findLast/predicate-call-parameters.js
  17. * - test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js
  18. * - test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js
  19. * - test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js
  20. * - test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js
  21. * - test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js
  22. * - test/built-ins/Array/prototype/some/15.4.4.17-7-1.js
  23. * - test/built-ins/Array/prototype/some/15.4.4.17-8-1.js
  24. * - test/built-ins/Array/prototype/every/15.4.4.16-7-1.js
  25. * - test/built-ins/Array/prototype/every/15.4.4.16-8-1.js
  26. * - test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js
  27. * - test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js
  28. * - test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js
  29. * - test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js
  30. * - test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js
  31. * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js
  32. * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js
  33. * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js
  34. * - test/built-ins/Array/prototype/flatMap/depth-always-one.js
  35. * - test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js
  36. * - test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js
  37. * - test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js
  38. * - test/built-ins/Array/prototype/sort/stability-5-elements.js
  39. * - test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js
  40. * - test/built-ins/Array/prototype/toSorted/comparefn-default.js
  41. * - test/built-ins/Array/prototype/toSorted/immutable.js
  42. * - test/built-ins/Array/prototype/toSorted/zero-or-one-element.js
  43. *
  44. * Copyright (C) 2015 the V8 project authors. All rights reserved.
  45. * Copyright (C) 2018 Mathias Bynens. All rights reserved.
  46. * Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
  47. * Copyright (C) 2021 Igalia, S.L. All rights reserved.
  48. * Copyright (C) 2021 Microsoft. All rights reserved.
  49. * Copyright (C) 2025 Google. All rights reserved.
  50. * Copyright (C) 2026 Garham Lee. All rights reserved.
  51. * Copyright (c) 2012 Ecma International. All rights reserved.
  52. * Copyright 2009 the Sputnik authors. All rights reserved.
  53. * Test262 portions are governed by the BSD license in LICENSE.test262.
  54. */
  55. import { describe, expect, test } from "bun:test"
  56. import { Effect } from "effect"
  57. import { CodeMode } from "../src/index.js"
  58. const value = async (code: string) => {
  59. const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
  60. if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
  61. return result.value
  62. }
  63. const cases = [
  64. {
  65. path: "test/built-ins/Array/prototype/map/15.4.4.19-8-1.js",
  66. code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.map((value) => { input[2] = 3; input[5] = 6; return 1 }); return [result.length, result[5] === undefined]`,
  67. expected: [5, true],
  68. },
  69. {
  70. path: "test/built-ins/Array/prototype/map/15.4.4.19-8-2.js",
  71. code: `const input = [1, 2, 3, 4, 5]; const result = input.map((value) => { input[4] = -1; return value > 0 ? 1 : 0 }); return [result.length, result[4]]`,
  72. expected: [5, 0],
  73. },
  74. {
  75. path: "test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js",
  76. code: `const input = []; input[10] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.map(() => { calls += 1; return 1 }); return [result.length, calls, 0 in result, 1 in result]`,
  77. expected: [10, 1, false, true],
  78. },
  79. {
  80. path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js",
  81. code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.filter(() => { input[2] = 3; input[5] = 6; return true }); return result`,
  82. expected: [1, 2, 3, 4, 5],
  83. },
  84. {
  85. path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js",
  86. code: `const input = [1, 2, 3, 4, 5]; return input.filter((value) => { input[2] = -1; input[4] = -1; return value > 0 })`,
  87. expected: [1, 2, 4],
  88. },
  89. {
  90. path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js",
  91. code: `const input = []; input[9] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.filter(() => { calls += 1; return false }); return [result, calls]`,
  92. expected: [[], 1],
  93. },
  94. {
  95. path: "test/built-ins/Array/prototype/find/predicate-call-parameters.js",
  96. code: `const input = [10, 20]; const seen = []; input.find((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
  97. expected: [
  98. [10, 0, true],
  99. [20, 1, true],
  100. ],
  101. },
  102. {
  103. path: "test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js",
  104. code: `return [1, 2, 3].find((value) => value > 1)`,
  105. expected: 2,
  106. },
  107. {
  108. path: "test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js",
  109. code: `return [1, 2, 3].find((value) => value > 4) === undefined`,
  110. expected: true,
  111. },
  112. {
  113. path: "test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js",
  114. code: `let calls = 0; const result = [].find(() => { calls += 1; return true }); return [result === undefined, calls]`,
  115. expected: [true, 0],
  116. },
  117. {
  118. path: "test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js",
  119. code: `const input = [10, 20]; const seen = []; input.findIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
  120. expected: [
  121. [10, 0, true],
  122. [20, 1, true],
  123. ],
  124. },
  125. {
  126. path: "test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js",
  127. code: `return [1, 2, 3].findIndex((value) => value > 1)`,
  128. expected: 1,
  129. },
  130. {
  131. path: "test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js",
  132. code: `return [1, 2, 3].findIndex((value) => value > 4)`,
  133. expected: -1,
  134. },
  135. {
  136. path: "test/built-ins/Array/prototype/findLast/predicate-call-parameters.js",
  137. code: `const input = [10, 20]; const seen = []; input.findLast((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
  138. expected: [
  139. [20, 1, true],
  140. [10, 0, true],
  141. ],
  142. },
  143. {
  144. path: "test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js",
  145. code: `return [1, 2, 3].findLast((value) => value < 3)`,
  146. expected: 2,
  147. },
  148. {
  149. path: "test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js",
  150. code: `return [1, 2, 3].findLast((value) => value > 4) === undefined`,
  151. expected: true,
  152. },
  153. {
  154. path: "test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js",
  155. code: `const input = [10, 20]; const seen = []; input.findLastIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
  156. expected: [
  157. [20, 1, true],
  158. [10, 0, true],
  159. ],
  160. },
  161. {
  162. path: "test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js",
  163. code: `return [1, 2, 3].findLastIndex((value) => value < 3)`,
  164. expected: 1,
  165. },
  166. {
  167. path: "test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js",
  168. code: `return [1, 2, 3].findLastIndex((value) => value > 4)`,
  169. expected: -1,
  170. },
  171. {
  172. path: "test/built-ins/Array/prototype/some/15.4.4.17-7-1.js",
  173. code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.some((value) => { input[2] = 3; seen.push(value); return false }); return [result, seen.includes(3)]`,
  174. expected: [false, true],
  175. },
  176. {
  177. path: "test/built-ins/Array/prototype/some/15.4.4.17-8-1.js",
  178. code: `return [].some(() => true)`,
  179. expected: false,
  180. },
  181. {
  182. path: "test/built-ins/Array/prototype/every/15.4.4.16-7-1.js",
  183. code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.every((value) => { input[2] = 3; seen.push(value); return true }); return [result, seen.includes(3)]`,
  184. expected: [true, true],
  185. },
  186. {
  187. path: "test/built-ins/Array/prototype/every/15.4.4.16-8-1.js",
  188. code: `return [].every(() => false)`,
  189. expected: true,
  190. },
  191. {
  192. path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js",
  193. code: `const input = [1, 2]; input[3] = 4; input[4] = 5; let calls = 0; input.forEach(() => { calls += 1; input[2] = 3; input[5] = 6 }); return calls`,
  194. expected: 5,
  195. },
  196. {
  197. path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js",
  198. code: `const input = [1, 2, 3]; const seen = []; input.forEach((value, index) => { seen.push(value); if (index === 0) input.pop() }); return seen`,
  199. expected: [1, 2],
  200. },
  201. {
  202. path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js",
  203. code: `const input = [1, 2]; input[3] = 4; input[4] = "5"; return input.reduce((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
  204. expected: "105",
  205. },
  206. {
  207. path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js",
  208. code: `let calls = 0; const result = [1].reduce(() => { calls += 1; return 2 }); return [result, calls]`,
  209. expected: [1, 0],
  210. },
  211. {
  212. path: "test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js",
  213. code: `const input = [1, 2, 3, 4, 5]; input.reduce(() => 1); return input`,
  214. expected: [1, 2, 3, 4, 5],
  215. },
  216. {
  217. path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js",
  218. code: `const input = ["1", 2]; input[3] = 4; input[4] = "5"; return input.reduceRight((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
  219. expected: "54321",
  220. },
  221. {
  222. path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js",
  223. code: `let calls = 0; const result = [1].reduceRight(() => { calls += 1; return 2 }); return [result, calls]`,
  224. expected: [1, 0],
  225. },
  226. {
  227. path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js",
  228. code: `const input = [1, 2, 3, 4, 5]; input.reduceRight(() => 1); return input`,
  229. expected: [1, 2, 3, 4, 5],
  230. },
  231. {
  232. path: "test/built-ins/Array/prototype/flatMap/depth-always-one.js",
  233. code: `return [1, 2, 3].flatMap((value) => [[value * 2]])`,
  234. expected: [[2], [4], [6]],
  235. },
  236. {
  237. path: "test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js",
  238. code: `try { [1, 2].flatMap(() => { throw "stop" }) } catch (error) { return error === "stop" } return false`,
  239. expected: true,
  240. },
  241. {
  242. path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js",
  243. code: `const input = []; input[2] = 0; input.pop(); input.sort(); return [input.length, input[0] === undefined, input[1] === undefined]`,
  244. expected: [2, true, true],
  245. },
  246. {
  247. path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js",
  248. code: `return ["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "M", "L", "K", "J", "I", "H", "G", "F", "E", "D", "C", "B", "A"].sort()`,
  249. expected: [
  250. "A",
  251. "B",
  252. "C",
  253. "D",
  254. "E",
  255. "F",
  256. "G",
  257. "H",
  258. "I",
  259. "J",
  260. "K",
  261. "L",
  262. "M",
  263. "n",
  264. "o",
  265. "p",
  266. "q",
  267. "r",
  268. "s",
  269. "t",
  270. "u",
  271. "v",
  272. "w",
  273. "x",
  274. "y",
  275. "z",
  276. ],
  277. },
  278. {
  279. path: "test/built-ins/Array/prototype/sort/stability-5-elements.js",
  280. code: `const input = [{ n: "A", r: 2 }, { n: "B", r: 3 }, { n: "C", r: 2 }, { n: "D", r: 3 }, { n: "E", r: 3 }]; return input.sort((left, right) => right.r - left.r).map((item) => item.n).join("")`,
  281. expected: "BDEAC",
  282. },
  283. {
  284. path: "test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js",
  285. code: `const mixed = [333, 33, 3, 222, 22, 2, 111, 11, 1]; return [[1, 2, 3, 4].toSorted((a, b) => a - b), [4, 3, 2, 1].toSorted((a, b) => a - b), mixed.toSorted((a, b) => a - b), [1, 2, 3, 4].toSorted((a, b) => b - a), [4, 3, 2, 1].toSorted((a, b) => b - a), mixed.toSorted((a, b) => b - a)]`,
  286. expected: [
  287. [1, 2, 3, 4],
  288. [1, 2, 3, 4],
  289. [1, 2, 3, 11, 22, 33, 111, 222, 333],
  290. [4, 3, 2, 1],
  291. [4, 3, 2, 1],
  292. [333, 222, 111, 33, 22, 11, 3, 2, 1],
  293. ],
  294. },
  295. {
  296. path: "test/built-ins/Array/prototype/toSorted/comparefn-default.js",
  297. code: `return [[1, 2, 3, 4].toSorted(), [4, 3, 2, 1].toSorted(), ["a", 2, 1, "z"].toSorted(), [333, 33, 3, 222, 22, 2, 111, 11, 1].toSorted()]`,
  298. expected: [
  299. [1, 2, 3, 4],
  300. [1, 2, 3, 4],
  301. [1, 2, "a", "z"],
  302. [1, 11, 111, 2, 22, 222, 3, 33, 333],
  303. ],
  304. },
  305. {
  306. path: "test/built-ins/Array/prototype/toSorted/immutable.js",
  307. code: `const input = [2, 0, 1]; const result = input.toSorted(); return [input, result !== input]`,
  308. expected: [[2, 0, 1], true],
  309. },
  310. {
  311. path: "test/built-ins/Array/prototype/toSorted/zero-or-one-element.js",
  312. code: `const zero = []; const one = [1]; const zeroResult = zero.toSorted(); const oneResult = one.toSorted(); return [zeroResult, oneResult, zeroResult !== zero, oneResult !== one]`,
  313. expected: [[], [1], true, true],
  314. },
  315. ] as const
  316. describe("Test262 Array callback adaptations", () => {
  317. for (const item of cases) {
  318. test(item.path, async () => {
  319. expect(await value(item.code)).toEqual(item.expected)
  320. })
  321. }
  322. })