Repository navigation
fix: draft relocated base objects at previously assigned indices in array-methods plugin - #1314
Open
OsamaAnsar wants to merge 1 commit into
Open
OsamaAnsar wants to merge 1 commit into
OsamaAnsar wants to merge 1 commit into
Conversation
…rray-methods plugin isRelocatedBaseRef (added in immerjs#1255) bails out when assigned_ has an entry for the index. But push, unshift, splice and index writes leave such entries behind, and after a later reverse/sort/unshift they describe an index, not the value that now sits there. A base object relocated onto one of those indices was returned from the get trap undrafted, so writing to it mutated the base state (and threw on a frozen base). baseRefs_ already identifies relocated base objects, and objects the user newly assigned are never in it, so the assigned_ check is not needed.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
With
enableArrayMethods(), a base object that an earlier call relocated onto an index that already has anassigned_entry comes back from the get trap undrafted. Writing to it mutates the base state, and throws on a frozen base.isRelocatedBaseRefinsrc/core/proxy.ts(added in #1255) returns false whenstate.assigned_?.get(prop)is set.push,unshift,spliceand index writes all put indices intoassigned_, and those entries stay there. Once the elements are reordered they describe an index, not the value that now sits there, so the guard skips base objects it should draft.Without the plugin the base is untouched. With a frozen base this throws
TypeError: Cannot assign to read only property 'n'. The same thing happens withpush+unshift,unshift+reverse, an index write +sort, andsplice+reverse. A more everyday shape:Fix
Drop the
assigned_check fromisRelocatedBaseRef.baseRefs_(a snapshot of the base elements, taken when the array is first reordered) already identifies a relocated base object, and a user-provided object is never in it, so it is still returned as-is. Already-drafted values are still skipped by thevalue[DRAFT_STATE]check. The positional check next to it (value === base_[prop]) already drafts a base object that is assigned back to its own index, so this is consistent with that.Test plan
Added to
__tests__/base.jsunder "mutating array methods" (so they run in every config, including with the plugin):find(),filter()andslice()afterpush()+sort()return drafts on a frozen baseWith only the
proxy.tschange reverted, 12 of the new tests fail (base mutated /Cannot assign to read only property), and they pass with the fix. The existing reverse/sort tests from #1255 still pass.Full
vitest run: 23 files, 3962 passed, 8 skipped.tsupbuild +vitest run --config vitest.config.build.tsalso passes. I did not runyarn test:flow.I also fuzzed 6000 random sequences of push/unshift/splice/index write/pop/shift/reverse/sort followed by a nested write, comparing plugin on vs. off. Before: 184 cases mutated the base and 184 threw on a frozen base. After: none, and the resulting state matches the plugin-off result in every case.