generator-test262.test.ts 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271
  1. /*
  2. * Portable portions adapted from Test262 at revision
  3. * 250f204f23a9249ff204be2baec29600faae7b75. Exact source paths are cited
  4. * beside the corresponding tests below.
  5. *
  6. * Copyright (C) 2013-2017 the V8 project authors. All rights reserved.
  7. * Copyright (C) 2018 Valerie Young. All rights reserved.
  8. * Copyright (C) 2020 Alexey Shvayka. All rights reserved.
  9. * Copyright (C) 2022 Kevin Gibbons. All rights reserved.
  10. * Copyright Ecma International. All rights reserved.
  11. * Test262 portions are governed by the BSD license in LICENSE.test262.
  12. */
  13. import { describe, expect, test } from "bun:test"
  14. import { Effect } from "effect"
  15. import { CodeMode } from "../src/index.js"
  16. const execute = (code: string) => Effect.runPromise(CodeMode.execute({ code, tools: {} }))
  17. const value = async (code: string) => {
  18. const result = await execute(code)
  19. if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
  20. return result.value
  21. }
  22. describe("confined generators", () => {
  23. // test/built-ins/GeneratorPrototype/next/return-yield-expr.js
  24. test("is lazy and preserves next(value), nested suspension, return, and exhaustion", async () => {
  25. expect(
  26. await value(`
  27. const events = []
  28. function* generate() {
  29. events.push("start")
  30. const received = yield 1 + (yield 2)
  31. return received
  32. }
  33. const iterator = generate()
  34. const before = events.slice()
  35. const first = iterator.next(99)
  36. const second = iterator.next(3)
  37. const third = iterator.next(7)
  38. const fourth = iterator.next(8)
  39. return [before, events, first, second, third, fourth]
  40. `),
  41. ).toEqual([
  42. [],
  43. ["start"],
  44. { value: 2, done: false },
  45. { value: 4, done: false },
  46. { value: 7, done: true },
  47. { value: null, done: true },
  48. ])
  49. })
  50. test("routes throw and return through catch and finally", async () => {
  51. expect(
  52. await value(`
  53. function* generate() {
  54. try {
  55. try { yield "try" } catch (error) { yield "caught " + error }
  56. } finally {
  57. yield "finally"
  58. }
  59. }
  60. const iterator = generate()
  61. return [iterator.next(), iterator.throw("boom"), iterator.return("done"), iterator.next()]
  62. `),
  63. ).toEqual([
  64. { value: "try", done: false },
  65. { value: "caught boom", done: false },
  66. { value: "finally", done: false },
  67. { value: "done", done: true },
  68. ])
  69. })
  70. test("throws into a suspended generator and after exhaustion", async () => {
  71. expect(
  72. await value(`
  73. function* generate() { yield 1 }
  74. const iterator = generate()
  75. iterator.next()
  76. let suspended
  77. let exhausted
  78. try { iterator.throw("first") } catch (error) { suspended = error }
  79. try { iterator.throw("second") } catch (error) { exhausted = error }
  80. return [suspended, exhausted, iterator.next()]
  81. `),
  82. ).toEqual(["first", "second", { value: null, done: true }])
  83. })
  84. test("rejects synchronous generator reentry", async () => {
  85. expect(
  86. await value(`
  87. let iterator
  88. function* generate() {
  89. try { iterator.next() } catch (error) { return error.name }
  90. }
  91. iterator = generate()
  92. return iterator.next()
  93. `),
  94. ).toEqual({ value: "TypeError", done: true })
  95. })
  96. test("delegates yield*, forwards next values, and receives the delegate return value", async () => {
  97. expect(
  98. await value(`
  99. function* inner() {
  100. const input = yield 1
  101. return input * 2
  102. }
  103. function* outer() {
  104. const result = yield* inner()
  105. return result + 1
  106. }
  107. const iterator = outer()
  108. return [iterator.next(), iterator.next(4)]
  109. `),
  110. ).toEqual([
  111. { value: 1, done: false },
  112. { value: 9, done: true },
  113. ])
  114. })
  115. test("delegates throw and return to custom iterators", async () => {
  116. expect(
  117. await value(`
  118. const calls = []
  119. let step = 0
  120. const delegate = {
  121. [Symbol.iterator]: () => delegate,
  122. next(...args) {
  123. calls.push(["next", args.length, args[0]])
  124. step += 1
  125. return step === 1 ? { value: "one", done: false } : { value: "end", done: true }
  126. },
  127. throw(value) {
  128. calls.push(["throw", value])
  129. return { value: "recovered", done: false }
  130. },
  131. return(value) {
  132. calls.push(["return", value])
  133. return { value: value + "!", done: true }
  134. },
  135. }
  136. function* generate() { return yield* delegate }
  137. const iterator = generate()
  138. const first = iterator.next()
  139. const second = iterator.throw("x")
  140. const third = iterator.return("stop")
  141. return [first, second, third, calls]
  142. `),
  143. ).toEqual([
  144. { value: "one", done: false },
  145. { value: "recovered", done: false },
  146. { value: "stop!", done: true },
  147. [
  148. ["next", 1, null],
  149. ["throw", "x"],
  150. ["return", "stop"],
  151. ],
  152. ])
  153. })
  154. test("uses the missing-throw delegation path for built-in iterables", async () => {
  155. expect(
  156. await value(`
  157. function* generate() { yield* [1, 2] }
  158. const iterator = generate()
  159. iterator.next()
  160. try { iterator.throw("boom") } catch (error) { return error.name }
  161. `),
  162. ).toBe("TypeError")
  163. })
  164. test("exposes only the appropriate iterator symbol and works in for...of", async () => {
  165. expect(
  166. await value(`
  167. function* generate() { yield 1; yield 2 }
  168. const iterator = generate()
  169. const symbols = [iterator[Symbol.iterator]() === iterator, iterator[Symbol.asyncIterator]]
  170. const values = []
  171. for (const item of iterator) values.push(item)
  172. return [symbols, values]
  173. `),
  174. ).toEqual([
  175. [true, null],
  176. [1, 2],
  177. ])
  178. })
  179. test("accepts generator methods as iterator acquisition results", async () => {
  180. expect(
  181. await value(`
  182. const sync = {
  183. *[Symbol.iterator]() { yield 1; yield 2 },
  184. }
  185. const asynchronous = {
  186. async *[Symbol.asyncIterator]() { yield 3; yield 4 },
  187. }
  188. const values = []
  189. for (const item of sync) values.push(item)
  190. for await (const item of asynchronous) values.push(item)
  191. return values
  192. `),
  193. ).toEqual([1, 2, 3, 4])
  194. })
  195. test("closes a generator when for...of exits abruptly", async () => {
  196. expect(
  197. await value(`
  198. const events = []
  199. function* generate() {
  200. try { yield 1; yield 2 } finally { events.push("closed") }
  201. }
  202. for (const item of generate()) break
  203. return events
  204. `),
  205. ).toEqual(["closed"])
  206. })
  207. test("async generator requests are promises and execute in request order", async () => {
  208. expect(
  209. await value(`
  210. const events = []
  211. async function* generate() {
  212. events.push("start")
  213. const input = yield Promise.resolve(1)
  214. events.push("received " + input)
  215. return Promise.resolve(3)
  216. }
  217. const iterator = generate()
  218. const first = iterator.next()
  219. const second = iterator.next(2)
  220. const third = iterator.next(4)
  221. const promiseFlags = [first instanceof Promise, second instanceof Promise, third instanceof Promise]
  222. return [promiseFlags, await Promise.all([first, second, third]), events]
  223. `),
  224. ).toEqual([
  225. [true, true, true],
  226. [
  227. { value: 1, done: false },
  228. { value: 3, done: true },
  229. { value: null, done: true },
  230. ],
  231. ["start", "received 2"],
  232. ])
  233. })
  234. test("keeps requests queued while a completed generator adopts return values", async () => {
  235. expect(
  236. await value(`
  237. const events = []
  238. let resolve
  239. const pending = new Promise((done) => { resolve = done })
  240. async function* generate() { return 1 }
  241. const iterator = generate()
  242. const first = iterator.next()
  243. const returned = iterator.return(pending)
  244. const later = first.then(() => iterator.next()).then(() => events.push("later"))
  245. returned.then(() => events.push("returned"))
  246. await first
  247. await Promise.resolve()
  248. const before = events.slice()
  249. resolve(9)
  250. await Promise.all([returned, later])
  251. return [before, events]
  252. `),
  253. ).toEqual([[], ["returned", "later"]])
  254. })
  255. test("serializes requests made after async generator exhaustion", async () => {
  256. expect(
  257. await value(`
  258. const events = []
  259. let resolve
  260. const pending = new Promise((done) => { resolve = done })
  261. async function* generate() { return 1 }
  262. const iterator = generate()
  263. await iterator.next()
  264. const returned = iterator.return(pending).then(() => events.push("returned"))
  265. const later = iterator.next().then(() => events.push("later"))
  266. await Promise.resolve()
  267. const before = events.slice()
  268. resolve(9)
  269. await Promise.all([returned, later])
  270. return [before, events]
  271. `),
  272. ).toEqual([[], ["returned", "later"]])
  273. })
  274. test("async generators adopt yielded, returned, and return-request promises", async () => {
  275. expect(
  276. await value(`
  277. async function* yielded() { yield Promise.resolve(1) }
  278. async function* returned() { return Promise.resolve(2) }
  279. async function* pending() { yield 0 }
  280. const first = yielded()
  281. const second = returned()
  282. const third = pending()
  283. const exhausted = returned()
  284. await third.next()
  285. await exhausted.next()
  286. return await Promise.all([
  287. first.next(),
  288. second.next(),
  289. third.return(Promise.resolve(3)),
  290. exhausted.return(Promise.resolve(4)),
  291. ])
  292. `),
  293. ).toEqual([
  294. { value: 1, done: false },
  295. { value: 2, done: true },
  296. { value: 3, done: true },
  297. { value: 4, done: true },
  298. ])
  299. })
  300. test("awaits return-request promises before injecting completion", async () => {
  301. expect(
  302. await value(`
  303. const events = []
  304. async function* generate() {
  305. try {
  306. yield 1
  307. } catch (error) {
  308. events.push("caught " + error)
  309. yield "recovered"
  310. } finally {
  311. events.push("finally")
  312. }
  313. }
  314. const iterator = generate()
  315. const first = await iterator.next()
  316. const returned = await iterator.return(Promise.reject("bad"))
  317. const beforeNext = events.slice()
  318. const last = await iterator.next()
  319. return [first, returned, beforeNext, last, events]
  320. `),
  321. ).toEqual([
  322. { value: 1, done: false },
  323. { value: "recovered", done: false },
  324. ["caught bad"],
  325. { value: null, done: true },
  326. ["caught bad", "finally"],
  327. ])
  328. })
  329. test("loop consumers call iterator next with no arguments", async () => {
  330. expect(
  331. await value(`
  332. const calls = []
  333. let syncStep = 0
  334. const sync = {
  335. [Symbol.iterator]: () => sync,
  336. next(...args) {
  337. calls.push(["sync", args.length])
  338. syncStep += 1
  339. return { value: syncStep, done: syncStep > 1 }
  340. },
  341. }
  342. let asyncStep = 0
  343. const asynchronous = {
  344. [Symbol.asyncIterator]: () => asynchronous,
  345. async next(...args) {
  346. calls.push(["async", args.length])
  347. asyncStep += 1
  348. return { value: asyncStep, done: asyncStep > 1 }
  349. },
  350. }
  351. for (const item of sync) {}
  352. for await (const item of asynchronous) {}
  353. return calls
  354. `),
  355. ).toEqual([
  356. ["sync", 0],
  357. ["sync", 0],
  358. ["async", 0],
  359. ["async", 0],
  360. ])
  361. })
  362. test("supports async generators in for await...of and keeps them out of for...of", async () => {
  363. expect(
  364. await value(`
  365. async function* generate() { yield 1; yield await Promise.resolve(2) }
  366. const values = []
  367. for await (const item of generate()) values.push(item)
  368. let name
  369. try { for (const item of generate()) {} } catch (error) { name = error.name }
  370. return [values, name]
  371. `),
  372. ).toEqual([[1, 2], "TypeError"])
  373. })
  374. test("keeps generator references opaque at the data boundary", async () => {
  375. const result = await execute(`function* generate() { yield 1 } return generate()`)
  376. expect(result.ok).toBe(false)
  377. if (result.ok) return
  378. expect(result.error.kind).toBe("InvalidDataValue")
  379. })
  380. // test/built-ins/GeneratorPrototype/return/from-state-suspended-start.js
  381. // test/built-ins/GeneratorPrototype/throw/from-state-suspended-start.js
  382. // test/built-ins/GeneratorPrototype/return/from-state-completed.js
  383. // test/built-ins/GeneratorPrototype/throw/from-state-completed.js
  384. test("honors sync return and throw in suspended-start and completed states", async () => {
  385. expect(
  386. await value(`
  387. const events = []
  388. function* generate() { events.push("body"); yield 1 }
  389. const returned = generate()
  390. const startReturn = returned.return(7)
  391. const afterReturn = returned.next()
  392. const thrown = generate()
  393. let startThrow
  394. try { thrown.throw("start") } catch (error) { startThrow = error }
  395. const afterThrow = thrown.next()
  396. let completedThrow
  397. try { thrown.throw("completed") } catch (error) { completedThrow = error }
  398. return [startReturn, afterReturn, startThrow, afterThrow, completedThrow, events]
  399. `),
  400. ).toEqual([
  401. { value: 7, done: true },
  402. { value: null, done: true },
  403. "start",
  404. { value: null, done: true },
  405. "completed",
  406. [],
  407. ])
  408. })
  409. // test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-promise.js
  410. // test/built-ins/AsyncGeneratorPrototype/throw/throw-suspendedStart.js
  411. // test/built-ins/AsyncGeneratorPrototype/return/return-state-completed.js
  412. // test/built-ins/AsyncGeneratorPrototype/throw/throw-state-completed.js
  413. test("honors async return and throw in suspended-start and completed states", async () => {
  414. expect(
  415. await value(`
  416. const events = []
  417. async function* generate() { events.push("body"); yield 1 }
  418. const returned = generate()
  419. const startReturn = await returned.return(Promise.resolve(7))
  420. const afterReturn = await returned.next()
  421. const thrown = generate()
  422. let startThrow
  423. try { await thrown.throw("start") } catch (error) { startThrow = error }
  424. const afterThrow = await thrown.next()
  425. let completedThrow
  426. try { await thrown.throw("completed") } catch (error) { completedThrow = error }
  427. return [startReturn, afterReturn, startThrow, afterThrow, completedThrow, events]
  428. `),
  429. ).toEqual([
  430. { value: 7, done: true },
  431. { value: null, done: true },
  432. "start",
  433. { value: null, done: true },
  434. "completed",
  435. [],
  436. ])
  437. })
  438. // test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-try-finally.js
  439. // test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-try-finally-return.js
  440. // test/built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-finally-throw.js
  441. test("runs finally yields and lets finally completions override requests", async () => {
  442. expect(
  443. await value(`
  444. async function* yielding() {
  445. try { yield 1 } finally { yield 2 }
  446. }
  447. async function* returning() {
  448. try { yield 1 } finally { return "override" }
  449. }
  450. async function* throwing() {
  451. try { yield 1 } finally { throw "override" }
  452. }
  453. const first = yielding()
  454. await first.next()
  455. const finallyYield = await first.return("sent")
  456. const preservedReturn = await first.next()
  457. const second = returning()
  458. await second.next()
  459. const overriddenReturn = await second.return("sent")
  460. const third = throwing()
  461. await third.next()
  462. let overriddenThrow
  463. try { await third.throw("sent") } catch (error) { overriddenThrow = error }
  464. return [finallyYield, preservedReturn, overriddenReturn, overriddenThrow]
  465. `),
  466. ).toEqual([{ value: 2, done: false }, { value: "sent", done: true }, { value: "override", done: true }, "override"])
  467. })
  468. // test/language/statements/async-generator/yield-promise-reject-next-catch.js
  469. // test/language/statements/async-generator/yield-promise-reject-next-yield-star-sync-iterator.js
  470. test("rejects yielded promises and closes direct and sync-delegating async generators", async () => {
  471. expect(
  472. await value(`
  473. async function* direct() { yield Promise.reject("direct") }
  474. async function* delegated() { yield* [Promise.reject("delegated"), "unreachable"] }
  475. const results = []
  476. for (const iterator of [direct(), delegated()]) {
  477. try { await iterator.next() } catch (error) { results.push(error) }
  478. results.push(await iterator.next())
  479. }
  480. return results
  481. `),
  482. ).toEqual(["direct", { value: null, done: true }, "delegated", { value: null, done: true }])
  483. })
  484. // test/built-ins/AsyncFromSyncIteratorPrototype/next/for-await-iterator-next-rejected-promise-close.js
  485. // test/built-ins/AsyncFromSyncIteratorPrototype/next/yield-iterator-next-rejected-promise-close.js
  486. // test/built-ins/AsyncFromSyncIteratorPrototype/throw/iterator-result-rejected-promise-close.js
  487. test("closes sync iterators when async-from-sync values reject", async () => {
  488. expect(
  489. await value(`
  490. const events = []
  491. function* loopSource() {
  492. try { yield Promise.reject("loop") } finally { events.push("loop close") }
  493. }
  494. let loopError
  495. try { for await (const item of loopSource()) {} } catch (error) { loopError = error }
  496. function* yieldSource() {
  497. try { yield Promise.reject("yield") } finally { events.push("yield close") }
  498. }
  499. async function* delegate() { yield* yieldSource() }
  500. let yieldError
  501. try { await delegate().next() } catch (error) { yieldError = error }
  502. const throwing = {
  503. [Symbol.iterator]: () => throwing,
  504. next: () => ({ value: 1, done: false }),
  505. throw: () => ({ value: Promise.reject("throw"), done: false }),
  506. return: () => { events.push("throw close"); return {} },
  507. }
  508. async function* throwDelegate() { yield* throwing }
  509. const iterator = throwDelegate()
  510. await iterator.next()
  511. let throwError
  512. try { await iterator.throw("sent") } catch (error) { throwError = error }
  513. return [loopError, yieldError, throwError, events]
  514. `),
  515. ).toEqual(["loop", "yield", "throw", ["loop close", "yield close", "throw close"]])
  516. })
  517. test("does not close rejected terminal or delegated return values", async () => {
  518. expect(
  519. await value(`
  520. const events = []
  521. const terminal = {
  522. [Symbol.iterator]: () => terminal,
  523. next: () => ({ value: Promise.reject("terminal"), done: true }),
  524. return: () => { events.push("terminal close"); return {} },
  525. }
  526. let terminalError
  527. try { for await (const item of terminal) {} } catch (error) { terminalError = error }
  528. let returnCount = 0
  529. const returned = {
  530. [Symbol.iterator]: () => returned,
  531. next: () => ({ value: 1, done: false }),
  532. return: () => {
  533. returnCount += 1
  534. return { value: Promise.reject("return"), done: false }
  535. },
  536. }
  537. async function* delegate() { yield* returned }
  538. const iterator = delegate()
  539. await iterator.next()
  540. let returnError
  541. try { await iterator.return("sent") } catch (error) { returnError = error }
  542. return [terminalError, returnError, returnCount, events]
  543. `),
  544. ).toEqual(["terminal", "return", 1, []])
  545. })
  546. test("serializes a mixed async next, throw, return, and next request queue", async () => {
  547. expect(
  548. await value(`
  549. async function* generate() {
  550. try {
  551. try { yield 1; yield 2 } catch (error) { yield "caught " + error }
  552. } finally {
  553. yield "finally"
  554. }
  555. }
  556. const iterator = generate()
  557. const first = iterator.next()
  558. const thrown = iterator.throw("x")
  559. const returned = iterator.return("done")
  560. const last = iterator.next()
  561. return await Promise.all([first, thrown, returned, last])
  562. `),
  563. ).toEqual([
  564. { value: 1, done: false },
  565. { value: "caught x", done: false },
  566. { value: "finally", done: false },
  567. { value: "done", done: true },
  568. ])
  569. })
  570. // test/built-ins/AsyncGeneratorPrototype/next/request-queue-order.js
  571. // test/built-ins/AsyncGeneratorPrototype/throw/request-queue-order-state-executing.js
  572. // test/built-ins/AsyncGeneratorPrototype/return/request-queue-order-state-executing.js
  573. test("orders async requests enqueued while generators are executing", async () => {
  574. expect(
  575. await value(`
  576. const events = []
  577. let returned, returnRequest
  578. async function* returnWhileExecuting() {
  579. returnRequest = returned.return(42).then((result) => events.push(["return", result]))
  580. yield 1
  581. }
  582. returned = returnWhileExecuting()
  583. const firstReturn = returned.next().then((result) => events.push(["first return", result]))
  584. await Promise.all([firstReturn, returnRequest])
  585. let thrown, throwRequest
  586. async function* throwWhileExecuting() {
  587. throwRequest = thrown.throw("boom").catch((error) => events.push(["throw", error]))
  588. yield 2
  589. }
  590. thrown = throwWhileExecuting()
  591. const firstThrow = thrown.next().then((result) => events.push(["first throw", result]))
  592. await Promise.all([firstThrow, throwRequest])
  593. async function* queued() { yield "first"; yield "second" }
  594. const iterator = queued()
  595. const first = iterator.next()
  596. const second = iterator.next()
  597. const third = iterator.next()
  598. await Promise.all([
  599. third.then(() => events.push("third")),
  600. second.then(() => events.push("second")),
  601. first.then(() => events.push("first")),
  602. ])
  603. return events
  604. `),
  605. ).toEqual([
  606. ["first return", { value: 1, done: false }],
  607. ["return", { value: 42, done: true }],
  608. ["first throw", { value: 2, done: false }],
  609. ["throw", "boom"],
  610. "first",
  611. "second",
  612. "third",
  613. ])
  614. })
  615. // test/language/statements/async-generator/yield-star-promise-not-unwrapped.js
  616. // test/language/statements/async-generator/yield-star-sync-next.js
  617. test("preserves async iterator promise values but unwraps async-from-sync values", async () => {
  618. expect(
  619. await value(`
  620. const asyncValue = Promise.resolve("async")
  621. const asynchronous = {
  622. [Symbol.asyncIterator]: () => asynchronous,
  623. next: () => ({ value: asyncValue, done: false }),
  624. }
  625. const syncValue = Promise.resolve("sync")
  626. const synchronous = {
  627. [Symbol.iterator]: () => synchronous,
  628. next: () => ({ value: syncValue, done: false }),
  629. }
  630. async function* delegate(value) { yield* value }
  631. const asyncResult = await delegate(asynchronous).next()
  632. const syncResult = await delegate(synchronous).next()
  633. return [asyncResult.value === asyncValue, await asyncResult.value, syncResult]
  634. `),
  635. ).toEqual([true, "async", { value: "sync", done: false }])
  636. })
  637. test("captures delegated result done before awaiting async-from-sync values", async () => {
  638. expect(
  639. await value(`
  640. const result = { value: Promise.resolve(1), done: false }
  641. result.value.then(() => { result.done = true })
  642. const source = {
  643. [Symbol.iterator]: () => source,
  644. next: () => result,
  645. }
  646. async function* delegate() { yield* source }
  647. return await delegate().next()
  648. `),
  649. ).toEqual({ value: 1, done: false })
  650. })
  651. // test/language/statements/async-generator/yield-star-async-next.js
  652. // test/language/statements/async-generator/yield-star-sync-return.js
  653. // test/language/statements/async-generator/yield-star-async-throw.js
  654. test("forwards next, return, and throw through sync and async yield delegates", async () => {
  655. expect(
  656. await value(`
  657. const calls = []
  658. const make = (symbol, label) => {
  659. let step = 0
  660. const iterator = {
  661. [symbol]: () => iterator,
  662. next(...args) {
  663. calls.push([label, "next", args.length, args[0]])
  664. step += 1
  665. return { value: step, done: false }
  666. },
  667. return(value) {
  668. calls.push([label, "return", value])
  669. return { value: value + "!", done: true }
  670. },
  671. throw(value) {
  672. calls.push([label, "throw", value])
  673. return { value: "caught " + value, done: false }
  674. },
  675. }
  676. return iterator
  677. }
  678. async function* delegate(iterator) { return yield* iterator }
  679. const sync = delegate(make(Symbol.iterator, "sync"))
  680. const asynchronous = delegate(make(Symbol.asyncIterator, "async"))
  681. const results = [await sync.next(9), await sync.next(2), await sync.throw("x"), await sync.return("stop")]
  682. results.push(await asynchronous.next(9), await asynchronous.next(3), await asynchronous.throw("y"))
  683. return [results, calls]
  684. `),
  685. ).toEqual([
  686. [
  687. { value: 1, done: false },
  688. { value: 2, done: false },
  689. { value: "caught x", done: false },
  690. { value: "stop!", done: true },
  691. { value: 1, done: false },
  692. { value: 2, done: false },
  693. { value: "caught y", done: false },
  694. ],
  695. [
  696. ["sync", "next", 1, null],
  697. ["sync", "next", 1, 2],
  698. ["sync", "throw", "x"],
  699. ["sync", "return", "stop"],
  700. ["async", "next", 1, null],
  701. ["async", "next", 1, 3],
  702. ["async", "throw", "y"],
  703. ],
  704. ])
  705. })
  706. // test/language/statements/async-generator/yield-star-sync-return.js
  707. // test/language/statements/async-generator/yield-star-async-throw.js
  708. test("continues delegation when return and throw report done false", async () => {
  709. expect(
  710. await value(`
  711. let returns = 0
  712. const synchronous = {
  713. [Symbol.iterator]: () => synchronous,
  714. next: () => ({ value: "next", done: false }),
  715. return(value) {
  716. returns += 1
  717. return { value: returns === 1 ? "return pending" : value, done: returns > 1 }
  718. },
  719. }
  720. let throws = 0
  721. const asynchronous = {
  722. [Symbol.asyncIterator]: () => asynchronous,
  723. next: () => ({ value: "next", done: false }),
  724. throw(value) {
  725. throws += 1
  726. return { value: throws === 1 ? "throw pending" : value, done: throws > 1 }
  727. },
  728. }
  729. async function* delegate(iterator) { return yield* iterator }
  730. const returned = delegate(synchronous)
  731. const thrown = delegate(asynchronous)
  732. return [
  733. await returned.next(),
  734. await returned.return("first"),
  735. await returned.return("second"),
  736. await thrown.next(),
  737. await thrown.throw("first"),
  738. await thrown.throw("second"),
  739. ]
  740. `),
  741. ).toEqual([
  742. { value: "next", done: false },
  743. { value: "return pending", done: false },
  744. { value: "second", done: true },
  745. { value: "next", done: false },
  746. { value: "throw pending", done: false },
  747. { value: "second", done: true },
  748. ])
  749. })
  750. // test/language/expressions/yield/star-rhs-iter-nrml-next-call-non-obj.js
  751. // test/language/expressions/yield/star-rhs-iter-thrw-thrw-call-non-obj.js
  752. // test/language/expressions/yield/star-rhs-iter-rtrn-rtrn-call-non-obj.js
  753. // test/language/statements/async-generator/yield-star-next-not-callable-number-throw.js
  754. test("rejects malformed yield delegate methods and iterator results", async () => {
  755. expect(
  756. await value(`
  757. const run = (iterator, operation) => {
  758. function* generate() {
  759. try { yield* iterator } catch (error) { return error.name }
  760. }
  761. const value = generate()
  762. const first = value.next()
  763. return operation === "next" ? first : value[operation]()
  764. }
  765. const iterable = (fields) => ({ [Symbol.iterator]: () => fields })
  766. const nextResult = run(iterable({ next: () => 1 }), "next")
  767. const throwResult = run(iterable({ next: () => ({ done: false }), throw: () => 1 }), "throw")
  768. const returnResult = run(iterable({ next: () => ({ done: false }), return: () => 1 }), "return")
  769. const badAsync = {
  770. [Symbol.asyncIterator]: () => ({ next: 1 }),
  771. }
  772. async function* asynchronous() {
  773. try { yield* badAsync } catch (error) { return error.name }
  774. }
  775. return [nextResult, throwResult, returnResult, await asynchronous().next()]
  776. `),
  777. ).toEqual([
  778. { value: "TypeError", done: true },
  779. { value: "TypeError", done: true },
  780. { value: "TypeError", done: true },
  781. { value: "TypeError", done: true },
  782. ])
  783. })
  784. // test/language/expressions/yield/captured-free-vars.js
  785. // test/language/statements/generators/dflt-params-ref-prior.js
  786. // test/language/expressions/generators/dflt-params-ref-prior.js
  787. // test/language/expressions/object/method-definition/generator-no-yield.js
  788. test("supports declaration, expression, and method forms with closures and parameters", async () => {
  789. expect(
  790. await value(`
  791. const captured = 4
  792. function* declaration(x, y = x, ...rest) { yield captured + y + rest[0] }
  793. const expression = function* (x, y = x) { yield captured + y }
  794. const object = { *method({ value }, extra = 1) { return captured + value + extra } }
  795. return [declaration(2, undefined, 3).next(), expression(5).next(), object.method({ value: 6 }).next()]
  796. `),
  797. ).toEqual([
  798. { value: 9, done: false },
  799. { value: 9, done: false },
  800. { value: 11, done: true },
  801. ])
  802. })
  803. // test/language/expressions/assignment/dstr/array-elem-iter-nrml-close.js
  804. test("steps holes and rest and closes array binding and assignment patterns early", async () => {
  805. expect(
  806. await value(`
  807. const events = []
  808. function* binding() {
  809. try { events.push("b1"); yield 1; events.push("b2"); yield 2; events.push("b3"); yield 3; yield 4 }
  810. finally { events.push("binding close") }
  811. }
  812. const [first, , third] = binding()
  813. function* assignment() {
  814. try { yield 5; yield 6; yield 7 }
  815. finally { events.push("assignment close") }
  816. }
  817. let head, rest
  818. ;[head, ...rest] = assignment()
  819. return [first, third, head, rest, events]
  820. `),
  821. ).toEqual([1, 3, 5, [6, 7], ["b1", "b2", "b3", "binding close", "assignment close"]])
  822. })
  823. // test/language/statements/variable/dstr/ary-ptrn-elem-id-init-throws.js
  824. // test/language/expressions/assignment/dstr/array-elem-iter-thrw-close-err.js
  825. test("closes on destructuring defaults and preserves the binding error over return failure", async () => {
  826. expect(
  827. await value(`
  828. const events = []
  829. const iterator = {
  830. [Symbol.iterator]: () => iterator,
  831. next: () => ({ value: undefined, done: false }),
  832. return: () => { events.push("close"); throw "close error" },
  833. }
  834. let caught
  835. try {
  836. const [value = (() => { throw "binding error" })()] = iterator
  837. } catch (error) { caught = error }
  838. return [caught, events]
  839. `),
  840. ).toEqual(["binding error", ["close"]])
  841. })
  842. test("does not close an exhausted iterator when a destructuring default fails", async () => {
  843. expect(
  844. await value(`
  845. const events = []
  846. const iterator = {
  847. [Symbol.iterator]: () => iterator,
  848. next: () => ({ done: true }),
  849. return: () => { events.push("close"); return {} },
  850. }
  851. try { const [item = (() => { throw "default" })()] = iterator } catch {}
  852. return events
  853. `),
  854. ).toEqual([])
  855. })
  856. test("consumes generators in array and argument spread without awaiting yielded promises", async () => {
  857. expect(
  858. await value(`
  859. function* values() { yield 1; yield Promise.resolve(2); yield 3 }
  860. const array = [...values()]
  861. const args = ((...items) => items)(...values())
  862. return [array[0], array[1] instanceof Promise, await array[1], args[2]]
  863. `),
  864. ).toEqual([1, true, 2, 3])
  865. })
  866. test("constructs Map, Set, and URLSearchParams from generators lazily", async () => {
  867. expect(
  868. await value(`
  869. const events = []
  870. function* pairs() { events.push(1); yield ["a", 1]; events.push(2); yield ["b", 2] }
  871. function* values() { yield 1; yield 1; yield 2 }
  872. const map = new Map(pairs())
  873. const set = new Set(values())
  874. const params = new URLSearchParams(pairs())
  875. return [map.get("b"), [...set], params.toString(), events]
  876. `),
  877. ).toEqual([2, [1, 2], "a=1&b=2", [1, 2, 1, 2]])
  878. })
  879. test("keeps built-in collection iteration live during callbacks", async () => {
  880. expect(
  881. await value(`
  882. const map = new Map([[1, 1], [2, 2]])
  883. const mapped = Array.from(map, (entry, index) => {
  884. if (index === 0) map.set(3, 3)
  885. return entry[0]
  886. })
  887. const set = new Set([1, 2])
  888. const grouped = Map.groupBy(set, (item, index) => {
  889. if (index === 0) set.add(3)
  890. return "items"
  891. })
  892. return [mapped, grouped.get("items")]
  893. `),
  894. ).toEqual([
  895. [1, 2, 3],
  896. [1, 2, 3],
  897. ])
  898. })
  899. test("keeps built-in collection iteration live in loops and yield delegation", async () => {
  900. expect(
  901. await value(`
  902. const map = new Map([[1, 1], [2, 2]])
  903. const mapValues = []
  904. for (const [key] of map) {
  905. mapValues.push(key)
  906. if (key === 1) map.set(3, 3)
  907. }
  908. const set = new Set([1, 2])
  909. const setValues = []
  910. for await (const item of set) {
  911. setValues.push(item)
  912. if (item === 1) set.add(3)
  913. }
  914. const params = new URLSearchParams("a=1&b=2")
  915. function* delegate() { yield* params }
  916. const iterator = delegate()
  917. const first = iterator.next()
  918. params.append("c", "3")
  919. return [mapValues, setValues, first, iterator.next(), iterator.next()]
  920. `),
  921. ).toEqual([
  922. [1, 2, 3],
  923. [1, 2, 3],
  924. { value: ["a", "1"], done: false },
  925. { value: ["b", "2"], done: false },
  926. { value: ["c", "3"], done: false },
  927. ])
  928. })
  929. test("preserves async-from-sync turns for built-in loop completion and close", async () => {
  930. expect(
  931. await value(`
  932. const completed = []
  933. Promise.resolve().then(() => completed.push("reaction"))
  934. for await (const item of []) {}
  935. completed.push("after")
  936. const closed = []
  937. for await (const item of [1]) {
  938. Promise.resolve().then(() => closed.push("reaction"))
  939. break
  940. }
  941. closed.push("after")
  942. return [completed, closed]
  943. `),
  944. ).toEqual([
  945. ["reaction", "after"],
  946. ["reaction", "after"],
  947. ])
  948. })
  949. test("reads iterator return only when closing", async () => {
  950. expect(
  951. await value(`
  952. const events = []
  953. const iterator = {
  954. [Symbol.iterator]: () => iterator,
  955. next() {
  956. iterator.return = () => { events.push("new"); return {} }
  957. return { value: 1, done: false }
  958. },
  959. return: () => { events.push("old"); return {} },
  960. }
  961. const [item] = iterator
  962. return [item, events]
  963. `),
  964. ).toEqual([1, ["new"]])
  965. })
  966. test("accepts iterable URLSearchParams entry pairs", async () => {
  967. expect(
  968. await value(`
  969. function* pair() { yield "a"; yield 1 }
  970. function* entries() { yield pair() }
  971. return new URLSearchParams(entries()).toString()
  972. `),
  973. ).toBe("a=1")
  974. })
  975. test("converts URLSearchParams pair elements before requesting the next", async () => {
  976. expect(
  977. await value(`
  978. const events = []
  979. function* pair() {
  980. try {
  981. events.push("first")
  982. yield (function* () {})()
  983. events.push("second")
  984. yield 2
  985. } finally { events.push("pair close") }
  986. }
  987. function* entries() {
  988. try { yield pair() } finally { events.push("outer close") }
  989. }
  990. let name
  991. try { new URLSearchParams(entries()) } catch (error) { name = error.name }
  992. return [events, name]
  993. `),
  994. ).toEqual([["first", "pair close", "outer close"], "Error"])
  995. })
  996. test("validates URLSearchParams pair lengths after converting the outer sequence", async () => {
  997. expect(
  998. await value(`
  999. const events = []
  1000. function* entries() {
  1001. try {
  1002. events.push("one")
  1003. yield ["a"]
  1004. events.push("two")
  1005. yield ["b", 2]
  1006. } finally { events.push("outer close") }
  1007. }
  1008. let name
  1009. try { new URLSearchParams(entries()) } catch (error) { name = error.name }
  1010. return [events, name]
  1011. `),
  1012. ).toEqual([["one", "two", "outer close"], "TypeError"])
  1013. })
  1014. test("closes entry constructors when a generator yields a malformed entry", async () => {
  1015. expect(
  1016. await value(`
  1017. const events = []
  1018. function* mapEntries() { try { yield ["a", 1]; yield null } finally { events.push("map close") } }
  1019. function* parameterEntries() { try { yield ["a"]; yield ["b", 2] } finally { events.push("params close") } }
  1020. const names = []
  1021. try { new Map(mapEntries()) } catch (error) { names.push(error.name) }
  1022. try { new URLSearchParams(parameterEntries()) } catch (error) { names.push(error.name) }
  1023. return [names, events]
  1024. `),
  1025. ).toEqual([
  1026. ["TypeError", "TypeError"],
  1027. ["map close", "params close"],
  1028. ])
  1029. })
  1030. // test/built-ins/Array/from/iter-map-fn-args.js
  1031. // test/built-ins/Array/from/iter-map-fn-err.js
  1032. test("interleaves Array.from mapping and closes when its mapper fails", async () => {
  1033. expect(
  1034. await value(`
  1035. const events = []
  1036. function* source() {
  1037. try { events.push("next 1"); yield 1; events.push("next 2"); yield 2 }
  1038. finally { events.push("close") }
  1039. }
  1040. const mapped = Array.from(source(), (item) => { events.push("map " + item); return item * 2 })
  1041. let caught
  1042. try { Array.from(source(), (item) => { throw "mapper " + item }) } catch (error) { caught = error }
  1043. return [mapped, caught, events]
  1044. `),
  1045. ).toEqual([[2, 4], "mapper 1", ["next 1", "map 1", "next 2", "map 2", "close", "next 1", "close"]])
  1046. })
  1047. test("reads array-like Array.from values immediately before mapping", async () => {
  1048. expect(
  1049. await value(`
  1050. const source = { 0: 1, 1: 2, length: 2 }
  1051. return Array.from(source, (item, index) => {
  1052. if (index === 0) source[1] = 9
  1053. return item
  1054. })
  1055. `),
  1056. ).toEqual([1, 9])
  1057. })
  1058. test("interleaves Object.groupBy and Map.groupBy callbacks and closes on callback failure", async () => {
  1059. expect(
  1060. await value(`
  1061. const events = []
  1062. function* source() { try { events.push("next"); yield 1; events.push("next"); yield 2 } finally { events.push("close") } }
  1063. const object = Object.groupBy(source(), (item) => { events.push("object " + item); return item % 2 })
  1064. const map = Map.groupBy(source(), (item) => { events.push("map " + item); return item % 2 })
  1065. let caught
  1066. try { Object.groupBy(source(), () => { throw "callback" }) } catch (error) { caught = error }
  1067. return [object, Object.fromEntries(map), caught, events]
  1068. `),
  1069. ).toEqual([
  1070. { 0: [2], 1: [1] },
  1071. { 0: [2], 1: [1] },
  1072. "callback",
  1073. ["next", "object 1", "next", "object 2", "close", "next", "map 1", "next", "map 2", "close", "next", "close"],
  1074. ])
  1075. })
  1076. test("consumes Promise combinator generators in order and observes rejections", async () => {
  1077. expect(
  1078. await value(`
  1079. function* items() { yield Promise.resolve(1); yield Promise.reject("bad"); yield 3 }
  1080. const settled = await Promise.allSettled(items())
  1081. let all, any
  1082. try { await Promise.all(items()) } catch (error) { all = error }
  1083. try { await Promise.any((function* () { yield Promise.reject("a"); yield Promise.reject("b") })()) }
  1084. catch (error) { any = error.errors }
  1085. const race = await Promise.race((function* () { yield 4; yield Promise.resolve(5) })())
  1086. return [settled, all, any, race]
  1087. `),
  1088. ).toEqual([
  1089. [
  1090. { status: "fulfilled", value: 1 },
  1091. { status: "rejected", reason: "bad" },
  1092. { status: "fulfilled", value: 3 },
  1093. ],
  1094. "bad",
  1095. ["a", "b"],
  1096. 4,
  1097. ])
  1098. })
  1099. test("finishes Promise combinator iterator consumption before returning the promise", async () => {
  1100. expect(
  1101. await value(`
  1102. const events = []
  1103. function* items() { events.push("first"); yield 1; events.push("second"); yield 2 }
  1104. const promise = Promise.all(items())
  1105. events.push("after call")
  1106. await promise
  1107. return events
  1108. `),
  1109. ).toEqual(["first", "second", "after call"])
  1110. })
  1111. // test/built-ins/Object/fromEntries/iterator-closed-for-null-entry.js
  1112. test("closes Object.fromEntries on malformed entries and consumes valid generators", async () => {
  1113. expect(
  1114. await value(`
  1115. const events = []
  1116. function* valid() { yield ["a", 1]; yield ["b", 2] }
  1117. function* invalid() { try { yield ["a", 1]; yield null; yield ["c", 3] } finally { events.push("close") } }
  1118. let name
  1119. try { Object.fromEntries(invalid()) } catch (error) { name = error.name }
  1120. return [Object.fromEntries(valid()), name, events]
  1121. `),
  1122. ).toEqual([{ a: 1, b: 2 }, "TypeError", ["close"]])
  1123. })
  1124. test("consumes AggregateError and Math.sumPrecise generators and closes on invalid numbers", async () => {
  1125. expect(
  1126. await value(`
  1127. const events = []
  1128. function* errors() { yield "a"; yield "b" }
  1129. function* numbers() { yield 1e30; yield 0.1; yield -1e30 }
  1130. function* invalid() { try { yield 1; yield "bad"; yield 2 } finally { events.push("close") } }
  1131. const aggregate = new AggregateError(errors(), "message")
  1132. let name
  1133. try { Math.sumPrecise(invalid()) } catch (error) { name = error.name }
  1134. return [aggregate.errors, aggregate.message, Math.sumPrecise(numbers()), name, events]
  1135. `),
  1136. ).toEqual([["a", "b"], "message", 0.1, "TypeError", ["close"]])
  1137. })
  1138. test("rejects async generators in every synchronous iterable consumer", async () => {
  1139. expect(
  1140. await value(`
  1141. async function* source() { yield ["a", 1] }
  1142. const checks = [
  1143. () => [...source()],
  1144. () => ((...items) => items)(...source()),
  1145. () => { const [item] = source(); return item },
  1146. () => Array.from(source()),
  1147. () => new Map(source()),
  1148. () => new Set(source()),
  1149. () => new URLSearchParams(source()),
  1150. () => Object.fromEntries(source()),
  1151. () => Object.groupBy(source(), (item) => item),
  1152. () => Math.sumPrecise(source()),
  1153. () => new AggregateError(source()),
  1154. ]
  1155. const names = []
  1156. for (const check of checks) {
  1157. try { check() } catch (error) { names.push(error.name) }
  1158. }
  1159. try { await Promise.all(source()) } catch (error) { names.push(error.name) }
  1160. return names
  1161. `),
  1162. ).toEqual(Array(12).fill("TypeError"))
  1163. })
  1164. // test/built-ins/Array/from/iter-get-iter-err.js
  1165. // test/built-ins/Array/from/iter-adv-err.js
  1166. test("does not close when iterator acquisition or next-result validation fails", async () => {
  1167. expect(
  1168. await value(`
  1169. const events = []
  1170. const acquisition = { [Symbol.iterator]: () => { events.push("acquire"); throw "acquisition" } }
  1171. const malformed = {
  1172. [Symbol.iterator]: () => malformed,
  1173. next: () => { events.push("next"); return 1 },
  1174. return: () => { events.push("close"); return {} },
  1175. }
  1176. for (const source of [acquisition, malformed]) {
  1177. try { Array.from(source) } catch {}
  1178. }
  1179. return events
  1180. `),
  1181. ).toEqual(["acquire", "next"])
  1182. })
  1183. test("reports synchronous iterator failures before queued promise reactions", async () => {
  1184. expect(
  1185. await value(`
  1186. const events = []
  1187. Promise.resolve().then(() => events.push("reaction"))
  1188. const iterator = {
  1189. [Symbol.iterator]: () => iterator,
  1190. next: () => { throw "next" },
  1191. }
  1192. try { Array.from(iterator) } catch { events.push("catch") }
  1193. await Promise.resolve()
  1194. return events
  1195. `),
  1196. ).toEqual(["catch", "reaction"])
  1197. })
  1198. })