Skip to content

fix: stack overflow on pre-existing Symbol circular references (#1106) - #1306

Open
dyk1454683243-sudo wants to merge 1 commit into
immerjs:mainfrom
dyk1454683243-sudo:cursor/fix-symbol-circular-refs-ea45
Open

dyk1454683243-sudo wants to merge 1 commit into
immerjs:mainfrom
dyk1454683243-sudo:cursor/fix-symbol-circular-refs-ea45

Conversation

@dyk1454683243-sudo

Copy link
Copy Markdown

Fixes #1106.

Problem

Starting in 10.0.4, Immer walks symbol keys. Objects that use symbols for child-to-parent links (skipped by JSON.stringify / Immer 9) then hit Maximum call stack size exceeded.

On current main, produce() itself is already safe: handleValue tracks visited objects. current() was not. It:

  1. Recursed into nested non-drafts with no cycle guard.
  2. Always used strict iteration for those nodes (strict was not passed down), so symbol links were followed even when setUseStrictIteration(false) (the default).

That is the remaining overflow for the issue’s data shape — e.g. produce(parent, d => { d.name = "x"; current(d) }).

Change

In current():

  • Remember objects already snapshotted (WeakMap) so circular refs (including symbol keys) reuse the snapshot instead of recursing forever.
  • Pass the same iteration mode into nested non-drafts.

No other finalization / copy behavior is changed.

Tests

__tests__/symbol-circular.js covers the issue’s cases:

  • produce on parent, nested child, and child-as-root
  • reading a pre-existing symbol without leaking drafts
  • current() after parent/child updates
  • self-referential symbol props
  • setUseStrictIteration(true) and strict shallow copy
  • symbols assigned inside a recipe (still no leaked drafts; produce leaks proxy objects when symbols are present #1087)

Notes

Circular references added inside a recipe were already fine; this is only about pre-existing symbol cycles. Plain (enumerable) circular refs remain unsupported, as documented.

current() recursively walked nested non-draft objects with no cycle
guard and always used strict iteration for those nodes. Pre-existing
Symbol child-to-parent links (immerjs#1106) therefore overflowed, even when
produce() itself was already safe via handleValue's handledSet.

Remember already-snapshotted objects and keep the same iteration mode
when descending, and add tests for produce() and current() on this
shape.

Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

"Maximum call stack exceeded" with circular references

2 participants