fix(codemode): bind generator parameters at the call, not the first next() (#50489)

This commit is contained in:
Aiden Cline
2026-09-21 22:42:10 -05:00
committed by GitHub
parent 60673aaef3
commit 6f8c5ae0aa
4 changed files with 35 additions and 101 deletions
+2 -2
View File
@@ -150,8 +150,8 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
- [x] Redeclaring a function in the same scope, or alongside a `var`, is allowed: the last declaration wins.
- [x] Generator functions have their own `prototype` (inheriting the shared generator prototype), so
`g() instanceof g` holds. Plain functions have none, since they cannot construct.
- [ ] Generator and async generator functions evaluate parameter defaults and destructuring at the first `next()`
rather than at the call, so their errors are not thrown synchronously.
- [x] Generator and async generator functions bind parameters (defaults, destructuring) at the call and defer only the
body to the first `next()`, so a bad argument throws synchronously from the call site, as in JS.
- [x] Synchronous and async generator declarations/expressions, `yield`, and `yield*`, including lazy bodies,
`next(value)`, `return(value)`, `throw(value)`, exhaustion, promise adoption, async request ordering,
`try`/`catch`/`finally`, and sync/async iterator symbols. Async `yield*` awaits values while adapting a sync
@@ -1659,14 +1659,14 @@ class Frame<R> {
const depth = Math.max(self.depth, site.depth) + 1
if (depth > MAX_CALL_DEPTH) throw rangeError("Maximum call stack size exceeded", node)
const invocation = new Frame(this.ctx, new ScopeStack([...fn.capturedScopes, new Map()]), depth)
const run = Effect.gen(function* () {
// Seed all parameters first so defaults cannot fall through to same-named outer bindings.
const paramScope = invocation.scopes.current()
for (const parameter of fn.parameters) {
for (const name of collectPatternNames(parameter)) {
paramScope.set(name, { mutable: true, value: undefined, initialized: false })
}
// Seed all parameters first so defaults cannot fall through to same-named outer bindings.
const paramScope = invocation.scopes.current()
for (const parameter of fn.parameters) {
for (const name of collectPatternNames(parameter)) {
paramScope.set(name, { mutable: true, value: undefined, initialized: false })
}
}
const bind = Effect.gen(function* () {
for (const [index, parameter] of fn.parameters.entries()) {
if (parameter.type === "RestElement") {
yield* invocation.declarePattern(
@@ -1680,7 +1680,8 @@ class Frame<R> {
}
yield* invocation.declarePattern(parameter, args[index], true, parameter, true)
}
})
const body = Effect.gen(function* () {
if (fn.body.type === "BlockStatement") {
invocation.scopes.push()
invocation.hoistVars(fn.body.body, paramScope)
@@ -1690,7 +1691,9 @@ class Frame<R> {
return yield* invocation.evaluateExpression(fn.body)
})
if (fn.generator) return Effect.succeed(this.createGenerator(invocation, run, fn))
// Generators bind parameters at the call and defer only the body to the first `next()`, as in JS.
if (fn.generator) return Effect.map(bind, () => this.createGenerator(invocation, body, fn))
const run = Effect.andThen(bind, body)
if (!fn.async) return run
return this.ctx.pending.createWithSelf((self) =>
Effect.flatMap(run, (value) => resolvePromiseValue(invocation.ctx, value, self)),
@@ -23,6 +23,27 @@ const value = async (code: string) => {
}
describe("confined generators", () => {
test("parameters are bound at the call; only the body waits for next()", async () => {
expect(
await value(`
const log = []
function* g(a = log.push("param")) { log.push("body"); yield a }
const it = g()
log.push("created")
it.next()
function* bad({ x } = null) { yield x }
async function* asyncBad({ x } = null) { yield x }
let failures = []
try { bad() } catch (error) { failures.push(error.constructor.name) }
try { asyncBad() } catch (error) { failures.push(error.constructor.name) }
return [log, failures]
`),
).toEqual([
["param", "created", "body"],
["TypeError", "TypeError"],
])
})
// test/built-ins/GeneratorPrototype/next/return-yield-expr.js
test("is lazy and preserves next(value), nested suspension, return, and exhaustion", async () => {
expect(
@@ -234,53 +234,8 @@ built-ins/Iterator/prototype/drop/limit-tonumber-throws.js # Expected a Test262
built-ins/Iterator/prototype/drop/limit-tonumber.js # Iterator.prototype.drop expects a non-negative count, received NaN.
built-ins/Iterator/prototype/take/limit-tonumber-throws.js # Expected a Test262Error but got a RangeError
built-ins/Iterator/prototype/take/limit-tonumber.js # Iterator.prototype.take expects a non-negative count, received NaN.
language/statements/async-generator/dflt-params-abrupt.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dflt-params-ref-later.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dflt-params-ref-self.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/async-generator/return-undefined-implicit-and-explicit.js # Actual ["tick 1", "tick 2", "g1 ret", "g2 ret", "g3 ret", "g4 ret"] and expected ["tick 1", "g1 ret"
language/statements/const/dstr/ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/for-await-of/async-func-decl-dstr-obj-empty-bool.js # TypeError: Object destructuring requires a data object or array value, received a boolean.
@@ -331,53 +286,8 @@ language/statements/function/S13.2_A4_T1.js # Unknown identifier '…'.
language/statements/function/S13.2_A4_T2.js # #1: typeof __gunc.prototype === '…'. Actual: typeof __gunc.prototype ===undefined
language/statements/function/dstr/ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/function/dstr/dflt-ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dflt-params-abrupt.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dflt-params-ref-later.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dflt-params-ref-self.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/dflt-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/generators/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/labeled/value-await-non-module.js # Failed to parse TypeScript: Expression expected.
language/statements/let/dstr/ary-init-iter-get-err-array-prototype.js # Expected a TypeError to be thrown but no exception was thrown at all
language/statements/return/S12.9_A1_T1.js # expected SyntaxError but the program ran