lexical-test262.test.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
  3. * - test/language/statements/let/global-use-before-initialization-in-prior-statement.js
  4. * - test/language/statements/let/block-local-use-before-initialization-in-prior-statement.js
  5. * - test/language/statements/const/global-use-before-initialization-in-prior-statement.js
  6. * - test/language/statements/const/block-local-use-before-initialization-in-prior-statement.js
  7. * - test/language/statements/let/block-local-use-before-initialization-in-declaration-statement.js
  8. * - test/language/statements/const/block-local-use-before-initialization-in-declaration-statement.js
  9. * - test/language/statements/let/block-local-closure-set-before-initialization.js
  10. * - test/language/statements/for-of/head-let-bound-names-fordecl-tdz.js
  11. * - test/language/statements/for-in/head-let-bound-names-fordecl-tdz.js
  12. * - test/language/statements/let/syntax/let-iteration-variable-is-freshly-allocated-for-each-iteration-single-let-binding.js
  13. * - test/language/statements/let/syntax/let-iteration-variable-is-freshly-allocated-for-each-iteration-multi-let-binding.js
  14. * - test/language/statements/for-of/head-let-fresh-binding-per-iteration.js
  15. * - test/language/statements/for-in/head-let-fresh-binding-per-iteration.js
  16. * - test/language/statements/for/scope-head-lex-open.js
  17. * - test/language/statements/for/scope-body-lex-open.js
  18. * - test/language/statements/switch/scope-lex-open-case.js
  19. * - test/language/statements/switch/scope-lex-close-case.js
  20. * - test/language/statements/function/dflt-params-ref-prior.js
  21. * - test/language/statements/function/dflt-params-ref-later.js
  22. * - test/language/statements/function/dflt-params-ref-self.js
  23. *
  24. * Copyright (C) 2011, 2014, 2016 the V8 project authors. All rights reserved.
  25. * Test262 portions are governed by the BSD license in LICENSE.test262.
  26. */
  27. import { describe, expect, test } from "bun:test"
  28. import { Effect } from "effect"
  29. import { CodeMode } from "../src/index.js"
  30. const value = async (code: string) => {
  31. const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
  32. if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
  33. return result.value
  34. }
  35. describe("Test262 lexical temporal dead zones", () => {
  36. test("program and block bindings exist before initialization", async () => {
  37. expect(
  38. await value(`
  39. const errors = []
  40. try { programLet; let programLet } catch (error) { errors.push(error.name) }
  41. try { programConst; const programConst = 1 } catch (error) { errors.push(error.name) }
  42. try { { blockLet; let blockLet } } catch (error) { errors.push(error.name) }
  43. try { { blockConst; const blockConst = 1 } } catch (error) { errors.push(error.name) }
  44. return errors
  45. `),
  46. ).toEqual(["ReferenceError", "ReferenceError", "ReferenceError", "ReferenceError"])
  47. })
  48. test("self-initialization and closure assignment observe the TDZ", async () => {
  49. expect(
  50. await value(`
  51. const errors = []
  52. try { { let item = item + 1 } } catch (error) { errors.push(error.name) }
  53. try { { const item = item + 1 } } catch (error) { errors.push(error.name) }
  54. try {
  55. {
  56. function assign() { item = 1 }
  57. assign()
  58. let item
  59. }
  60. } catch (error) { errors.push(error.name) }
  61. return errors
  62. `),
  63. ).toEqual(["ReferenceError", "ReferenceError", "ReferenceError"])
  64. })
  65. test("for-of and for-in bound names are in the head TDZ", async () => {
  66. expect(
  67. await value(`
  68. const errors = []
  69. try { let item = [1]; for (let item of item) {} } catch (error) { errors.push(error.name) }
  70. try { let item = { value: 1 }; for (let item in item) {} } catch (error) { errors.push(error.name) }
  71. return errors
  72. `),
  73. ).toEqual(["ReferenceError", "ReferenceError"])
  74. })
  75. })
  76. describe("Test262 lexical loop environments", () => {
  77. test("classic for creates fresh single and multiple bindings", async () => {
  78. expect(
  79. await value(`
  80. const single = []
  81. for (let index = 0; index < 5; ++index) single.push(() => index)
  82. const left = []
  83. const right = []
  84. for (let first = 0, second = 10; first < 5; ++first, ++second) {
  85. left.push(() => first)
  86. right.push(() => second)
  87. }
  88. return [
  89. single.map((read) => read()),
  90. left.map((read) => read()),
  91. right.map((read) => read()),
  92. ]
  93. `),
  94. ).toEqual([
  95. [0, 1, 2, 3, 4],
  96. [0, 1, 2, 3, 4],
  97. [10, 11, 12, 13, 14],
  98. ])
  99. })
  100. test("for-of and for-in create fresh bindings", async () => {
  101. expect(
  102. await value(`
  103. const values = []
  104. for (let item of [1, 2, 3]) values.push(() => item)
  105. const keys = {}
  106. for (let key in { first: 1, second: 2, third: 3 }) keys[key] = () => key
  107. return [values.map((read) => read()), keys.first(), keys.second(), keys.third()]
  108. `),
  109. ).toEqual([[1, 2, 3], "first", "second", "third"])
  110. })
  111. test("classic for separates declaration and per-iteration environments", async () => {
  112. expect(
  113. await value(`
  114. let before
  115. let testRead
  116. let bodyRead
  117. let updateRead
  118. let run = true
  119. for (
  120. let item = "outside", ignored = before = () => item;
  121. run && (item = "inside", testRead = () => item);
  122. updateRead = () => item
  123. ) bodyRead = () => item, run = false
  124. return [before(), testRead(), bodyRead(), updateRead()]
  125. `),
  126. ).toEqual(["outside", "inside", "inside", "inside"])
  127. })
  128. })
  129. describe("Test262 switch and parameter environments", () => {
  130. test("switch creates its lexical environment after the discriminant", async () => {
  131. expect(
  132. await value(`
  133. let item = "outside"
  134. let discriminantRead
  135. let selectorRead
  136. let statementRead
  137. switch ((discriminantRead = () => item, null)) {
  138. case (selectorRead = () => item, null):
  139. statementRead = () => item
  140. let item = "inside"
  141. }
  142. return [discriminantRead(), selectorRead(), statementRead()]
  143. `),
  144. ).toEqual(["outside", "inside", "inside"])
  145. })
  146. test("all switch cases share one lexical environment that closes afterward", async () => {
  147. expect(
  148. await value(`
  149. let item = "outside"
  150. let firstRead
  151. let secondRead
  152. switch (null) {
  153. case null:
  154. let item = "inside"
  155. firstRead = () => item
  156. case null:
  157. secondRead = () => item
  158. }
  159. return [firstRead(), secondRead(), item]
  160. `),
  161. ).toEqual(["inside", "inside", "outside"])
  162. })
  163. test("parameter defaults see prior bindings but not self or later bindings", async () => {
  164. expect(
  165. await value(`
  166. function prior(first, second = first, third = second) { return [first, second, third] }
  167. function later(first = second, second) { return first }
  168. function self(item = item) { return item }
  169. function failure(run) {
  170. try { return run() } catch (error) { return error.name }
  171. }
  172. return [prior(3), failure(later), failure(self)]
  173. `),
  174. ).toEqual([[3, 3, 3], "ReferenceError", "ReferenceError"])
  175. })
  176. })