Revert "fix: ensure deep mutation ownership widening" (#11155)

* Revert "fix: ensure deep mutation ownership widening (#11094)"

This reverts commit 8578857332.

* don't delete the changeset, it's already merged
pull/11161/head
Rich Harris 9 months ago committed by GitHub
parent be842425f5
commit dc6a8398d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -2,7 +2,6 @@
import { STATE_SYMBOL } from '../constants.js'; import { STATE_SYMBOL } from '../constants.js';
import { untrack } from '../runtime.js'; import { untrack } from '../runtime.js';
import { get_descriptors } from '../utils.js';
/** @type {Record<string, Array<{ start: Location, end: Location, component: Function }>>} */ /** @type {Record<string, Array<{ start: Location, end: Location, component: Function }>>} */
const boundaries = {}; const boundaries = {};
@ -92,107 +91,49 @@ export function mark_module_end() {
} }
} }
let add_owner_visited = new Set();
/** /**
* *
* @param {any} object * @param {any} object
* @param {any} owner * @param {any} owner
*/ */
export function add_owner(object, owner) { export function add_owner(object, owner) {
// Needed because ownership addition can invoke getters on a proxy,
// calling add_owner anew, so just keeping the set as part of
// add_owner_to_object would not be enough.
const prev = add_owner_visited;
try {
add_owner_visited = new Set(add_owner_visited);
untrack(() => { untrack(() => {
add_owner_to_object(object, owner, add_owner_visited); add_owner_to_object(object, owner);
}); });
} finally {
add_owner_visited = prev;
}
} }
/** /**
* @param {any} object * @param {any} object
* @param {Function} owner * @param {Function} owner
* @param {Set<any>} visited
*/ */
function add_owner_to_object(object, owner, visited) { function add_owner_to_object(object, owner) {
if (visited.has(object)) return;
visited.add(object);
if (object?.[STATE_SYMBOL]?.o && !object[STATE_SYMBOL].o.has(owner)) { if (object?.[STATE_SYMBOL]?.o && !object[STATE_SYMBOL].o.has(owner)) {
object[STATE_SYMBOL].o.add(owner); object[STATE_SYMBOL].o.add(owner);
for (const key in object) {
add_owner_to_object(object[key], owner);
}
} }
// Not inside previous if-block; there could be normal objects in-between
traverse_for_owners(object, (nested) => add_owner_to_object(nested, owner, visited));
} }
let strip_owner_visited = new Set();
/** /**
* @param {any} object * @param {any} object
*/ */
export function strip_owner(object) { export function strip_owner(object) {
// Needed because ownership stripping can invoke getters on a proxy,
// calling strip_owner anew, so just keeping the set as part of
// strip_owner_from_object would not be enough.
const prev = strip_owner_visited;
try {
untrack(() => { untrack(() => {
strip_owner_from_object(object, strip_owner_visited); strip_owner_from_object(object);
}); });
} finally {
strip_owner_visited = prev;
}
} }
/** /**
* @param {any} object * @param {any} object
* @param {Set<any>} visited
*/ */
function strip_owner_from_object(object, visited) { function strip_owner_from_object(object) {
if (visited.has(object)) return;
visited.add(object);
if (object?.[STATE_SYMBOL]?.o) { if (object?.[STATE_SYMBOL]?.o) {
object[STATE_SYMBOL].o = null; object[STATE_SYMBOL].o = null;
}
// Not inside previous if-block; there could be normal objects in-between
traverse_for_owners(object, (nested) => strip_owner_from_object(nested, visited));
}
/**
* @param {any} object
* @param {(obj: any) => void} cb
*/
function traverse_for_owners(object, cb) {
if (typeof object === 'object' && object !== null && !(object instanceof EventTarget)) {
for (const key in object) { for (const key in object) {
cb(object[key]); strip_owner(object[key]);
}
// deal with state on classes
const proto = Object.getPrototypeOf(object);
if (
proto !== Object.prototype &&
proto !== Array.prototype &&
proto !== Map.prototype &&
proto !== Set.prototype &&
proto !== Date.prototype
) {
const descriptors = get_descriptors(proto);
for (let key in descriptors) {
const get = descriptors[key].get;
if (get) {
try {
cb(object[key]);
} catch (e) {
// continue
}
}
}
} }
} }
} }

@ -1,35 +0,0 @@
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 btn = target.querySelector('button');
await btn?.click();
await tick();
assert.deepEqual(warnings.length, 0);
}
});

@ -1,25 +0,0 @@
<script lang="ts">
import { setContext } from 'svelte';
import Sub from './sub.svelte';
class Person1 {
value = $state({ person: 'John', age: 33 })
}
const class_nested_state = $state(new Person1());
class Person2 {
person = $state('John');
age = $state(33);
}
const state_nested_class = $state({ value: new Person2() });
const nested_state = $state({ person: 'John', age: 33 });
setContext('foo', {
nested_state,
get class_nested_state() { return class_nested_state },
get state_nested_class() { return state_nested_class }
})
</script>
<Sub {class_nested_state} {state_nested_class} {nested_state} />

@ -1,11 +0,0 @@
<script>
import { getContext } from "svelte";
const foo = getContext('foo')
</script>
<button onclick={() => {
foo.class_nested_state.value.age++;
foo.state_nested_class.value.age++;
foo.nested_state.age++;
}}>mutate</button>
Loading…
Cancel
Save