Skip to content

Commit b12e5c9

Browse files
committed
fix: Set finalization can get stuck in a loop, fixes #628
1 parent b1c6a8e commit b12e5c9

File tree

2 files changed

+32
-3
lines changed

2 files changed

+32
-3
lines changed

__tests__/regressions.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,29 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) {
116116
expect(state2.length).toBe(3)
117117
expect(state2).toEqual([undefined, "v1", "v2"])
118118
})
119+
120+
test("#628 set removal hangs", () => {
121+
let arr = []
122+
let set = new Set([arr])
123+
124+
let result = produce(set, draft1 => {
125+
produce(draft1, draft2 => {
126+
draft2.delete(arr)
127+
})
128+
})
129+
expect(result).toEqual(new Set([[]])) // N.B. this outcome doesn't seem not correct, but then again,
130+
// double produce without return looks iffy as well, so not sure what the expected outcome in the
131+
// original report was
132+
})
133+
134+
test("#628 - 2 set removal hangs", () => {
135+
let arr = []
136+
let set = new Set([arr])
137+
138+
let result = produce(set, draft2 => {
139+
draft2.delete(arr)
140+
})
141+
expect(result).toEqual(new Set())
142+
})
119143
})
120144
}

src/core/finalize.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,14 @@ function finalize(rootScope: ImmerScope, value: any, path?: PatchPath) {
8787
state.type_ === ProxyTypeES5Object || state.type_ === ProxyTypeES5Array
8888
? (state.copy_ = shallowCopy(state.draft_))
8989
: state.copy_
90-
// finalize all children of the copy
91-
each(result as any, (key, childValue) =>
92-
finalizeProperty(rootScope, state, result, key, childValue, path)
90+
// Finalize all children of the copy
91+
// For sets we clone before iterating, otherwise we can get in endless loop due to modifying during iteration, see #628
92+
// Although the original test case doesn't seem valid anyway, so if this in the way we can turn the next line
93+
// back to each(result, ....)
94+
each(
95+
state.type_ === ProxyTypeSet ? new Set(result) : result,
96+
(key, childValue) =>
97+
finalizeProperty(rootScope, state, result, key, childValue, path)
9398
)
9499
// everything inside is frozen, we can freeze here
95100
maybeFreeze(rootScope, result, false)

0 commit comments

Comments
 (0)