mirror of https://github.com/sveltejs/svelte
fix: don't reuse proxies when state symbol refers to stale value (#10343)
* fix: don't reuse proxies when state symbol refers to stale value When somebody copies over the state symbol property onto a new object (you can retrieve the symbol by using `Reflect.ownKeys(...)`), it was wrongfully assumed that it always relates to the current value. This PR adds an additional check that this is actually the case. This also adds a dev time warning when an object is frozen but contains a state property, which hints at a bug in user land. fixes #10316 * lint * rename * remove warning * update test * changeset --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> Co-authored-by: Rich Harris <richard.a.harris@gmail.com>pull/10357/head
parent
bce4f3f01c
commit
e75c9acd18
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: only reuse state proxies that belong to the current value
|
@ -0,0 +1,16 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
compileOptions: {
|
||||
dev: true
|
||||
},
|
||||
|
||||
html: `<button>state1.value: a state2.value: a</button>`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
const btn = target.querySelector('button');
|
||||
|
||||
await btn?.click();
|
||||
assert.htmlEqual(target.innerHTML, `<button>state1.value: b state2.value: b</button>`);
|
||||
}
|
||||
});
|
@ -0,0 +1,28 @@
|
||||
<script>
|
||||
let foo = { value: 'a' }
|
||||
let state1 = $state(foo);
|
||||
let state2 = $state(foo);
|
||||
</script>
|
||||
|
||||
<button onclick={() => {
|
||||
let new_state1 = {};
|
||||
let new_state2 = {};
|
||||
// This contains Symbol.$state and Symbol.$readonly and we can't do anything against it,
|
||||
// because it's called on the original object, not our state proxy
|
||||
Reflect.ownKeys(foo).forEach(k => {
|
||||
new_state1[k] = foo[k];
|
||||
new_state2[k] = foo[k];
|
||||
});
|
||||
new_state1.value = 'b';
|
||||
new_state2.value = 'b';
|
||||
// $.proxy will see that Symbol.$state exists on this object already, which shouldn't result in a stale value
|
||||
state1 = new_state1;
|
||||
// $.proxy can't look into Symbol.$state because of the frozen object
|
||||
state2 = Object.freeze(new_state2);
|
||||
}}
|
||||
>
|
||||
state1.value: {state1.value}
|
||||
state2.value: {state2.value}
|
||||
</button>
|
||||
|
||||
|
Loading…
Reference in new issue