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