Skip to content

fix: dynamic Function/Generator/AsyncFunction constructor coercion gaps#476

Open
dowdiness wants to merge 3 commits into
mainfrom
fix/dynamic-constructor-coercion-gaps
Open

fix: dynamic Function/Generator/AsyncFunction constructor coercion gaps#476
dowdiness wants to merge 3 commits into
mainfrom
fix/dynamic-constructor-coercion-gaps

Conversation

@dowdiness

@dowdiness dowdiness commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • Gap 1 (GeneratorFunction/AsyncFunction coercion order): Fix param/body coercion to apply left-to-right per §20.2.1.1.1 step 5a, and use interp.to_js_string() (ES ToString) instead of MoonBit .to_string() for non-string args.

  • Gap 2 (GeneratorFunction yield-in-params): Detect YieldExpression in generator function params per §20.2.1.1.1 step 17a. Two-part fix: (a) push generator context before parsing params in parse_generator_decl so yield in defaults is parsed as YieldExpr not an identifier; (b) walk the AST via params_contain_yield to raise SyntaxError when found.

  • Gap 3 (Function .prototype.constructor wiring): Add .prototype object with back-pointer .constructor to dynamically created functions per §20.2.1.1.1 step 27a. Applied to FuncDecl, FuncDeclExt, and the fallback arm in builtins_function.mbt.

  • Gap 4 (AsyncFunction [[Prototype]]): Set AsyncFunction.[[Prototype]] = Function (the constructor) per §27.7.2, so Object.getPrototypeOf(AsyncFunction) === Function.

Test results

  • built-ins/GeneratorFunction: 42/42 (was 40/42 before; instance-yield-expr-in-param.js now passes in both modes)
  • built-ins/AsyncFunction: 34/34 (was 32/34; AsyncFunction-is-subclass.js now passes)
  • built-ins/Function: 796/824 (remaining failures are pre-existing issues unrelated to this PR)

Test plan

  • make test262-filter FILTER="built-ins/GeneratorFunction" → 42/42
  • make test262-filter FILTER="built-ins/AsyncFunction" → 34/34
  • No new built-ins/Function failures
  • CI green

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved generator and async function handling for more spec-compliant behavior.
    • Generator function parameters now correctly reject invalid yield usage in default values.
    • Dynamically created functions now expose more accurate prototype and constructor behavior.
    • Fixed parsing context handling so generator-related syntax is processed consistently and errors no longer leave the parser in a bad state.

dowdiness and others added 2 commits June 27, 2026 00:23
…rcion gaps

§20.2.1.1.1 CreateDynamicFunction — three gaps addressed:

1. GeneratorFunction/AsyncFunction: coerce params left-to-right (step 5a)
   and use interp.to_js_string() (ES ToString) for non-string arguments
   instead of MoonBit .to_string().

2. Function: add .prototype.constructor wiring to dynamically created
   functions so that `new (new Function(...))` correctly sets up the
   prototype chain per §20.2.1.1.1 step 27a.

3. AsyncFunction [[Prototype]]: set to the Function constructor per
   §27.7.2, so Object.getPrototypeOf(AsyncFunction) === Function.

4. GeneratorFunction params: add raw-source-text check for `yield` as
   a keyword in param strings (§20.2.1.1.1 step 17a). The parser does
   not enter generator context for params, so yield is parsed as an
   identifier; the keyword check is applied before parse.

All 42 GeneratorFunction and 34 AsyncFunction test262 tests now pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per §15.5.1 of the ES spec, default parameter expressions in a
generator function's formal parameter list share the generator's
yield context. Push generator context before parsing params so
that `yield` in default values is correctly parsed as YieldExpr
rather than as an identifier.

This enables the AST-based §20.2.1.1.1 step 17a check to work
correctly via params_contain_yield(), replacing the previous
raw-text keyword scan which had false positives for `yield` in
comments, string literals, and object binding property keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@dowdiness, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 12 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d35c660d-4f88-4e37-b54a-748cc26b2fdf

📥 Commits

Reviewing files that changed from the base of the PR and between 245bf2b and e13e186.

📒 Files selected for processing (3)
  • interpreter/runtime/generator.mbt
  • interpreter/stdlib/builtins_function.mbt
  • parser/stmt.mbt
📝 Walkthrough

Walkthrough

The PR updates generator constructor parsing to reject yield in parameter defaults, changes Function constructor outputs to create per-function prototype objects with constructor back-references, and aligns AsyncFunction prototype setup with the intrinsic Function constructor.

Changes

Constructor semantics updates

Layer / File(s) Summary
Generator parameter handling
parser/stmt.mbt, interpreter/runtime/generator.mbt
parse_generator_decl now shares generator context with parameter parsing and clears it on errors; generator helpers detect yield in parameter defaults, use interp.to_js_string(...) for constructor arguments, and reject generator parameters containing yield.
Function prototype objects
interpreter/stdlib/builtins_function.mbt
Function constructor paths now create per-function prototype objects, include them in the stamped function properties, and add constructor back-references for parsed and fallback function objects.
AsyncFunction prototype setup
interpreter/runtime/async.mbt
setup_async_function_constructor now resolves Function from the environment and updates the async constructor object’s prototype field when the constructor object is present.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • dowdiness/js_engine#26: The generator parameter yield handling and generator-context parsing changes continue the generator/yield work in that PR.
  • dowdiness/js_engine#405: The AsyncFunction prototype-chain update touches the same async constructor wiring area as that PR.

Poem

🐰 I hopped through constructors, neat and spry,
yield in the carrot patch got a stern “nope” reply.
Prototype burrows now have a proper door,
And async moonbeams point to Function once more.
Thump-thump, the engine hums so bright tonight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixing construction and coercion gaps for dynamic Function, GeneratorFunction, and AsyncFunction.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dynamic-constructor-coercion-gaps

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 245bf2b56c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread parser/stmt.mbt
// §15.5.1: params and body share generator context so that `yield` in
// default-parameter expressions is parsed as YieldExpression, enabling
// the §20.2.1.1.1 step 17a Contains check via the AST.
self.push_generator_context(true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow yield as a destructuring property name

With the generator context pushed before parsing parameters, parse_params_ext() now also parses object binding-pattern property names under in_generator(). That path uses expect_ident(), so valid declarations such as function* g({yield: x}) {} (and dynamic GeneratorFunction("{yield: x}", "")) now throw even though yield is only a property name, not a binding identifier or a yield expression. The parser needs to keep rejecting yield bindings/expressions while still allowing keyword property names in binding patterns.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results

Run: https://github.com/dowdiness/js_engine/actions/runs/28274914476

startup/tiny_program is the PR #153 / issue #141 guardrail for built-in realm-stamping startup cost.

Stage summary

stage benchmarks total mean slowest benchmark slowest mean noisy rows
startup 3 2.558 ms startup/tiny_program 1.288 ms 0
frontend 7 0.922 ms pipeline/parse_heavy 0.510 ms 2
execution 26 16019.605 ms exec/fibonacci_30 14476.399 ms 1

Focused bytecode base-vs-head comparison

Base-vs-head deltas are reporting-only. Negative delta and PR/base < 1.00x mean the PR is faster; interpret high-CV or noisy rows cautiously.

benchmark stage base mean PR mean delta PR/base base CV PR CV noisy
baseline/bytecode/closure_factory execution 13.744 ms 15.880 ms +15.5% 1.16x 8.7% 6.8% no
pipeline/bytecode/evaluate execution 9.094 ms 9.857 ms +8.4% 1.08x 3.8% 3.0% no
isolate/bytecode/call_frame execution 7.507 ms 7.890 ms +5.1% 1.05x 1.4% 3.2% no
isolate/bytecode/runtime_helpers execution 10.728 ms 11.472 ms +6.9% 1.07x 1.0% 3.9% no
isolate/bytecode/local_access execution 37.881 ms 38.211 ms +0.9% 1.01x 0.7% 0.5% no
isolate/bytecode/env_access execution 38.073 ms 38.079 ms +0.0% 1.00x 0.5% 0.7% no
isolate/bytecode/captured_access execution 39.302 ms 38.732 ms -1.4% 0.99x 1.6% 2.2% no
isolate/bytecode/dispatch_stack execution 24.758 ms 25.002 ms +1.0% 1.01x 2.7% 0.5% no

Base-vs-head comparison

benchmark stage base mean PR mean delta PR/base base CV PR CV noisy
startup/tiny_program startup 1.221 ms 1.288 ms +5.5% 1.06x 5.5% 5.2% no
lexer/small frontend 0.036 ms 0.035 ms -3.4% 0.97x 26.8% 28.2% base, PR
lexer/large frontend 0.297 ms 0.296 ms -0.1% 1.00x 1.7% 0.9% no
exec/fibonacci_30 execution 14350.960 ms 14476.399 ms +0.9% 1.01x 0.3% 0.9% no
exec/property_chain execution 15.932 ms 16.209 ms +1.7% 1.02x 13.0% 10.3% no
startup/phase/parse_tiny frontend 0.002 ms 0.002 ms +1.7% 1.02x 0.8% 0.8% no
startup/phase/new_interpreter startup 1.240 ms 1.270 ms +2.4% 1.02x 17.8% 4.9% base
startup/phase/execute_preparsed_tiny execution 0.001 ms 0.001 ms +5.7% 1.06x 0.9% 0.9% no
startup/phase/event_loop_drain_empty startup 0.000 ms 0.000 ms +3.1% 1.03x 0.9% 1.1% no
startup/phase/result_stringify_output execution 0.000 ms 0.000 ms +1.6% 1.02x 0.4% 0.5% no
exec/array_map_filter execution 20.383 ms 21.817 ms +7.0% 1.07x 10.9% 12.4% no
exec/closure_factory execution 29.763 ms 33.514 ms +12.6% 1.13x 5.5% 4.7% no
baseline/closure_legacy/closure_factory execution 28.634 ms 31.468 ms +9.9% 1.10x 10.7% 5.9% no
baseline/bytecode/closure_factory execution 13.744 ms 15.880 ms +15.5% 1.16x 8.7% 6.8% no
isolate/bytecode/dispatch_stack execution 24.758 ms 25.002 ms +1.0% 1.01x 2.7% 0.5% no
isolate/bytecode/local_access execution 37.881 ms 38.211 ms +0.9% 1.01x 0.7% 0.5% no
isolate/bytecode/env_access execution 38.073 ms 38.079 ms +0.0% 1.00x 0.5% 0.7% no
isolate/bytecode/captured_access execution 39.302 ms 38.732 ms -1.4% 0.99x 1.6% 2.2% no
isolate/bytecode/call_frame execution 7.507 ms 7.890 ms +5.1% 1.05x 1.4% 3.2% no
isolate/bytecode/runtime_helpers execution 10.728 ms 11.472 ms +6.9% 1.07x 1.0% 3.9% no
isolate/bytecode/property_get execution 45.251 ms 44.484 ms -1.7% 0.98x 1.2% 2.0% no
isolate/bytecode/property_set execution 41.327 ms 40.979 ms -0.8% 0.99x 1.7% 1.9% no
isolate/bytecode/method_call execution 8.369 ms 8.828 ms +5.5% 1.05x 1.0% 4.9% no
isolate/bytecode/object_literal execution 13.505 ms 13.943 ms +3.2% 1.03x 1.2% 1.0% no
isolate/bytecode/array_literal execution 14.784 ms 15.363 ms +3.9% 1.04x 1.6% 3.5% no
exec/for_of execution 6.629 ms 6.717 ms +1.3% 1.01x 14.4% 6.8% no
exec/arithmetic_loop execution 1003.125 ms 1056.451 ms +5.3% 1.05x 0.4% 0.3% no
exec/object_construction execution 7.732 ms 8.014 ms +3.6% 1.04x 5.5% 5.3% no
exec/string_ops execution 2.110 ms 1.993 ms -5.5% 0.94x 17.9% 18.6% base, PR
pipeline/exec/lex frontend 0.031 ms 0.030 ms -3.4% 0.97x 1.0% 0.8% no
pipeline/exec/parse frontend 0.026 ms 0.027 ms +5.9% 1.06x 3.7% 4.1% no
pipeline/exec/evaluate execution 27.213 ms 29.857 ms +9.7% 1.10x 9.5% 6.1% no
pipeline/closure_legacy/evaluate execution 25.319 ms 28.444 ms +12.3% 1.12x 4.5% 4.1% no
pipeline/bytecode/compile frontend 0.022 ms 0.021 ms -1.5% 0.99x 29.3% 26.6% base, PR
pipeline/bytecode/evaluate execution 9.094 ms 9.857 ms +8.4% 1.08x 3.8% 3.0% no
pipeline/parse_heavy frontend 0.497 ms 0.510 ms +2.6% 1.03x 5.3% 5.6% no

Mean-time chart (log scale)

benchmark stage mean chart
startup/tiny_program startup 1.288 ms ##
lexer/small frontend 0.035 ms ⚠ #
lexer/large frontend 0.296 ms #
exec/fibonacci_30 execution 14476.399 ms ##############################
exec/property_chain execution 16.209 ms ########
startup/phase/parse_tiny frontend 0.002 ms #
startup/phase/new_interpreter startup 1.270 ms ##
startup/phase/execute_preparsed_tiny execution 0.001 ms #
startup/phase/event_loop_drain_empty startup 0.000 ms #
startup/phase/result_stringify_output execution 0.000 ms #
exec/array_map_filter execution 21.817 ms #########
exec/closure_factory execution 33.514 ms ###########
baseline/closure_legacy/closure_factory execution 31.468 ms ##########
baseline/bytecode/closure_factory execution 15.880 ms ########
isolate/bytecode/dispatch_stack execution 25.002 ms ##########
isolate/bytecode/local_access execution 38.211 ms ###########
isolate/bytecode/env_access execution 38.079 ms ###########
isolate/bytecode/captured_access execution 38.732 ms ###########
isolate/bytecode/call_frame execution 7.890 ms ######
isolate/bytecode/runtime_helpers execution 11.472 ms #######
isolate/bytecode/property_get execution 44.484 ms ###########
isolate/bytecode/property_set execution 40.979 ms ###########
isolate/bytecode/method_call execution 8.828 ms #######
isolate/bytecode/object_literal execution 13.943 ms ########
isolate/bytecode/array_literal execution 15.363 ms ########
exec/for_of execution 6.717 ms ######
exec/arithmetic_loop execution 1056.451 ms #####################
exec/object_construction execution 8.014 ms ######
exec/string_ops execution 1.993 ms ⚠ ###
pipeline/exec/lex frontend 0.030 ms #
pipeline/exec/parse frontend 0.027 ms #
pipeline/exec/evaluate execution 29.857 ms ##########
pipeline/closure_legacy/evaluate execution 28.444 ms ##########
pipeline/bytecode/compile frontend 0.021 ms ⚠ #
pipeline/bytecode/evaluate execution 9.857 ms #######
pipeline/parse_heavy frontend 0.510 ms #

Closure-conversion comparison

  • unavailable

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
parser/stmt.mbt (1)

188-211: 🩺 Stability & Availability | 🟡 Minor

Extended error recovery path leaks context on secondary parsing failures.

The generator/async contexts are pushed at lines 9–10. If parse_params() fails with a specific error (e.g., "REST_PARAM_FOUND"), the code attempts recovery by calling parse_params_ext() and parse_block_body() (lines 17–19). However, if either of these calls raises an exception, the explicit pop operations (lines 20–21) are bypassed, and the exception propagates.

The surrounding catch block only wraps the initial parse_params() call; it does not guard the recovery logic inside the Failure arm. Consequently, any failure in the recovery path leaves the contexts pushed at lines 9–10 on the stack.

The standard success path and the initial parse_params failure paths correctly balance pushes and pops, but this asymmetry corrupts the parser's context stack depth during error recovery.

@@ -16,16 +16,23 @@ fn Parser::parse_generator_decl(self : Parser) -> `@ast.Stmt` raise Error {
       if msg == "REST_PARAM_FOUND" ||
         msg == "DEFAULT_PARAM_FOUND" ||
         msg == "DESTRUCTURE_PARAM_FOUND" {
         self.pos = saved_pos
-        let (ext_params, rest_param) = self.parse_params_ext()
-        // Context already pushed; parse body within the same context.
-        let body = self.parse_block_body()
-        self.pop_async_context()
-        self.pop_generator_context()
-        return GeneratorDeclExt(
-          name,
-          ext_params,
-          rest_param,
-          body,
-          tok.loc,
-          self.consume_source_text(tok.loc.offset),
-        )
+        // Guard recovery calls to ensure context cleanup on error.
+        try {
+          let (ext_params, rest_param) = self.parse_params_ext()
+          let body = self.parse_block_body()
+          return GeneratorDeclExt(
+            name,
+            ext_params,
+            rest_param,
+            body,
+            tok.loc,
+            self.consume_source_text(tok.loc.offset),
+          )
+        }
+        // Ensure contexts are popped if recovery fails.
+        finally {
+          self.pop_async_context()
+          self.pop_generator_context()
+        }
       } else {
         self.pop_async_context()
         self.pop_generator_context()
         raise `@errors.SyntaxError`(message=msg)
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@parser/stmt.mbt` around lines 188 - 211, The recovery branch in the
generator/async parsing flow leaves contexts pushed if parse_params_ext() or
parse_block_body() throws, so update the parse logic in stmt.mbt to ensure
pop_async_context() and pop_generator_context() run for every recovery attempt.
Wrap the recovery work inside the same GeneratorDeclExt path with a cleanup
guard or nested catch/finally-style handling so the contexts are always unwound
before re-raising, and verify both the success and failure paths in the
parse_params()/parse_params_ext() flow stay balanced.
🧹 Nitpick comments (1)
interpreter/stdlib/builtins_function.mbt (1)

425-440: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the repeated prototype/back-reference setup.

The FuncDecl, FuncDeclExt, and fallback paths now duplicate the same prototype object, "prototype" descriptor, and "constructor" descriptor wiring. A small helper would keep these spec-sensitive attributes synchronized if one branch changes later.

Also applies to: 490-503, 507-522, 569-582, 588-646

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@interpreter/stdlib/builtins_function.mbt` around lines 425 - 440, The
prototype/back-reference wiring is duplicated across the FuncDecl, FuncDeclExt,
and fallback paths, so extract that repeated setup into a shared helper. Move
the common creation of the prototype object plus the "prototype" and
"constructor" descriptors into one place, and have the affected branches call it
from builtins_function.mbt so the spec-sensitive attributes stay consistent if
one branch changes. Use the existing function/class symbols around the
function-creation flow to centralize this logic without altering behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@interpreter/runtime/generator.mbt`:
- Around line 364-371: The generator-parameter yield validation in
GeneratorDeclExt is only catching the current AST shape and misses destructuring
and computed-key parameter forms that can also contain yield expressions. Update
params_contain_yield to recursively inspect all parameter patterns used by
generator params, or add equivalent validation in the GeneratorDeclExt handling
so every nested default, destructuring, and computed-key case is rejected
consistently before codegen.
- Around line 176-185: params_contain_yield only checks Param.default_val, so it
misses yield expressions nested inside destructuring patterns. Update this
verifier in generator.mbt to also traverse p.pattern and recurse through
ArrayPat/ObjectPat trees, checking DefaultPat expressions, PropPat.default_val,
and PropPat.computed_key with expr_contains_yield. Keep the existing top-level
default_val check, but add pattern traversal so GeneratorFunction cases like
destructured defaults and computed keys are rejected as SyntaxError.

---

Outside diff comments:
In `@parser/stmt.mbt`:
- Around line 188-211: The recovery branch in the generator/async parsing flow
leaves contexts pushed if parse_params_ext() or parse_block_body() throws, so
update the parse logic in stmt.mbt to ensure pop_async_context() and
pop_generator_context() run for every recovery attempt. Wrap the recovery work
inside the same GeneratorDeclExt path with a cleanup guard or nested
catch/finally-style handling so the contexts are always unwound before
re-raising, and verify both the success and failure paths in the
parse_params()/parse_params_ext() flow stay balanced.

---

Nitpick comments:
In `@interpreter/stdlib/builtins_function.mbt`:
- Around line 425-440: The prototype/back-reference wiring is duplicated across
the FuncDecl, FuncDeclExt, and fallback paths, so extract that repeated setup
into a shared helper. Move the common creation of the prototype object plus the
"prototype" and "constructor" descriptors into one place, and have the affected
branches call it from builtins_function.mbt so the spec-sensitive attributes
stay consistent if one branch changes. Use the existing function/class symbols
around the function-creation flow to centralize this logic without altering
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f47893d-f629-4173-8e46-1469856bc354

📥 Commits

Reviewing files that changed from the base of the PR and between ec4b801 and 245bf2b.

📒 Files selected for processing (4)
  • interpreter/runtime/async.mbt
  • interpreter/runtime/generator.mbt
  • interpreter/stdlib/builtins_function.mbt
  • parser/stmt.mbt

Comment thread interpreter/runtime/generator.mbt
Comment thread interpreter/runtime/generator.mbt
… params

Four targeted fixes from review feedback on PR #476:

1. parse_object_pattern: use expect_ident_name() for property keys so that
   `yield` is accepted as a LiteralPropertyName (ES §12.2.6) even in generator
   context. Fixes GF("{yield: x}", "return x") incorrectly throwing SyntaxError.

2. parse_generator_decl: wrap parse_params_ext() and parse_block_body() calls in
   inline catch blocks so push_generator_context / push_async_context are always
   popped on error, eliminating the parser context-stack leak.

3. pattern_contains_yield: new helper that recursively walks ArrayPat, ObjectPat,
   DefaultPat, and PropPat (computed_key + default_val) so that yield expressions
   nested inside destructuring defaults (e.g. {x = yield} or {[yield]: x}) are
   correctly caught. Property name strings are intentionally excluded.

4. builtins_function.mbt: extract make_function_prototype / wire_constructor_to_prototype
   helpers to deduplicate the identical prototype-object creation and
   constructor back-reference wiring across the FuncDecl, FuncDeclExt, and
   fallback return paths.

GeneratorFunction: 42/42 test262, 2249/2249 unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e13e186b7b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread parser/stmt.mbt
// §15.5.1: params and body share generator context so that `yield` in
// default-parameter expressions is parsed as YieldExpression, enabling
// the §20.2.1.1.1 step 17a Contains check via the AST.
self.push_generator_context(true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset generator context for nested function params

Because this context is now pushed before parsing the outer parameter list, it also leaks into default-value subexpressions. parse_func_expr() and object-method parsing do not reset generator context until after their own parameters, so valid code such as GeneratorFunction("a = function(yield) {}", "") or GeneratorFunction("a = {m(yield){}}", "") now throws while parsing the nested non-generator parameter even though that nested function has its own yield context.

Useful? React with 👍 / 👎.

Comment thread parser/stmt.mbt
// `yield` in generator context) is valid as an object binding property key.
_ =>
(
self.expect_ident_name(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject keyword shorthand bindings in object patterns

This correctly allows keywords as property keys for forms like {yield: x}, but the no-colon branch below reuses the same string as IdentPat(key). For shorthand object binding patterns the key is also a BindingIdentifier, so invalid code such as var {if} = {}; or function* g({yield}) {} is now accepted instead of throwing; only the explicit keyword: binding form should accept arbitrary IdentifierName keys.

Useful? React with 👍 / 👎.

| AsyncGeneratorExprExt(_, _, _, _, _, _)
| ArrowFunc(_, _, _, _)
| ArrowFuncExt(_, _, _, _, _)
| ClassExpr(_, _, _, _, _) => false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Traverse class expressions when checking generator params

A class expression is not a nested function boundary for the parameter Contains(YieldExpression) check: its heritage and computed member names are evaluated in the surrounding parameter scope. Returning false for the entire ClassExpr lets cases like GeneratorFunction("x = class extends (yield) {}", "") or computed class keys bypass the new params_contain_yield check and be accepted instead of raising the required SyntaxError at construction.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant