From 4b59ef3c41cb5fd6ed4036265db239ecdc4cf569 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:00:39 +0200 Subject: [PATCH] fix: widen ownership when sub state is assigned to new state (#11217) Ownership was not widened when assigning a sub state to a different top level state. The set of owners for the state was zero because the owner was on the original parent, but that one was reset to null because it's now the top level of a different state. That meant that there was no owner but also no parent to check for the owner, which is an invalid combination resulting in a nullpointer (and also potentially false positive warnings in other situations). fixes #11204 --- .changeset/weak-frogs-bow.md | 5 +++ .../src/internal/client/dev/ownership.js | 4 +- packages/svelte/src/internal/client/proxy.js | 3 ++ .../Child.svelte | 10 +++++ .../_config.js | 41 +++++++++++++++++++ .../main.svelte | 13 ++++++ 6 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 .changeset/weak-frogs-bow.md create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte diff --git a/.changeset/weak-frogs-bow.md b/.changeset/weak-frogs-bow.md new file mode 100644 index 0000000000..f897249948 --- /dev/null +++ b/.changeset/weak-frogs-bow.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: widen ownership when sub state is assigned to new state diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index bef9fb8437..30dceab486 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -127,8 +127,8 @@ export function add_owner(object, owner, global = false) { } /** - * @param {import('#client').ProxyMetadata | null} from - * @param {import('#client').ProxyMetadata} to + * @param {import('#client').ProxyMetadata | null} from + * @param {import('#client').ProxyMetadata} to */ export function widen_ownership(from, to) { if (to.owners === null) { diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 17f8e1b2cd..136458c99d 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -38,6 +38,9 @@ export function proxy(value, immutable = true, parent = null) { // someone copied the state symbol using `Reflect.ownKeys(...)` if (metadata.t === value || metadata.p === value) { if (DEV) { + // Since original parent relationship gets lost, we need to copy over ancestor owners + // into current metadata. The object might still exist on both, so we need to widen it. + widen_ownership(metadata, metadata); metadata.parent = parent; } diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte new file mode 100644 index 0000000000..220638c07a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte @@ -0,0 +1,10 @@ + + +
{item?.name}
+ diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js new file mode 100644 index 0000000000..df3ca08f03 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js @@ -0,0 +1,41 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +/** @type {typeof console.warn} */ +let warn; + +/** @type {any[]} */ +let warnings = []; + +export default test({ + compileOptions: { + dev: true + }, + + before_test: () => { + warn = console.warn; + + console.warn = (...args) => { + warnings.push(...args); + }; + }, + + after_test: () => { + console.warn = warn; + warnings = []; + }, + + async test({ assert, target }) { + const [btn1, btn2] = target.querySelectorAll('button'); + + btn1.click(); + await tick(); + + assert.deepEqual(warnings.length, 0); + + btn2.click(); + await tick(); + + assert.deepEqual(warnings.length, 1); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte new file mode 100644 index 0000000000..8e8343790b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte @@ -0,0 +1,13 @@ + + + +