|
|
|
|
@ -4,6 +4,7 @@ import { get_descriptors, get_prototype_of, index_of } from '../shared/utils.js'
|
|
|
|
|
import {
|
|
|
|
|
destroy_block_effect_children,
|
|
|
|
|
destroy_effect_children,
|
|
|
|
|
effect_tracking,
|
|
|
|
|
execute_effect_teardown
|
|
|
|
|
} from './reactivity/effects.js';
|
|
|
|
|
import {
|
|
|
|
|
@ -11,13 +12,12 @@ import {
|
|
|
|
|
MAYBE_DIRTY,
|
|
|
|
|
CLEAN,
|
|
|
|
|
DERIVED,
|
|
|
|
|
UNOWNED,
|
|
|
|
|
DESTROYED,
|
|
|
|
|
BRANCH_EFFECT,
|
|
|
|
|
STATE_SYMBOL,
|
|
|
|
|
BLOCK_EFFECT,
|
|
|
|
|
ROOT_EFFECT,
|
|
|
|
|
DISCONNECTED,
|
|
|
|
|
CONNECTED,
|
|
|
|
|
REACTION_IS_UPDATING,
|
|
|
|
|
STALE_REACTION,
|
|
|
|
|
ERROR_VALUE,
|
|
|
|
|
@ -137,10 +137,6 @@ export function set_update_version(value) {
|
|
|
|
|
update_version = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we are working with a get() chain that has no active container,
|
|
|
|
|
// to prevent memory leaks, we skip adding the reaction.
|
|
|
|
|
export let skip_reaction = false;
|
|
|
|
|
|
|
|
|
|
export function increment_write_version() {
|
|
|
|
|
return ++write_version;
|
|
|
|
|
}
|
|
|
|
|
@ -158,55 +154,18 @@ export function is_dirty(reaction) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & DERIVED) {
|
|
|
|
|
reaction.f &= ~WAS_MARKED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((flags & MAYBE_DIRTY) !== 0) {
|
|
|
|
|
var dependencies = reaction.deps;
|
|
|
|
|
var is_unowned = (flags & UNOWNED) !== 0;
|
|
|
|
|
|
|
|
|
|
if (flags & DERIVED) {
|
|
|
|
|
reaction.f &= ~WAS_MARKED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dependencies !== null) {
|
|
|
|
|
var i;
|
|
|
|
|
var dependency;
|
|
|
|
|
var is_disconnected = (flags & DISCONNECTED) !== 0;
|
|
|
|
|
var is_unowned_connected = is_unowned && active_effect !== null && !skip_reaction;
|
|
|
|
|
var length = dependencies.length;
|
|
|
|
|
|
|
|
|
|
// If we are working with a disconnected or an unowned signal that is now connected (due to an active effect)
|
|
|
|
|
// then we need to re-connect the reaction to the dependency, unless the effect has already been destroyed
|
|
|
|
|
// (which can happen if the derived is read by an async derived)
|
|
|
|
|
if (
|
|
|
|
|
(is_disconnected || is_unowned_connected) &&
|
|
|
|
|
(active_effect === null || (active_effect.f & DESTROYED) === 0)
|
|
|
|
|
) {
|
|
|
|
|
var derived = /** @type {Derived} */ (reaction);
|
|
|
|
|
var parent = derived.parent;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < length; i++) {
|
|
|
|
|
dependency = dependencies[i];
|
|
|
|
|
|
|
|
|
|
// We always re-add all reactions (even duplicates) if the derived was
|
|
|
|
|
// previously disconnected, however we don't if it was unowned as we
|
|
|
|
|
// de-duplicate dependencies in that case
|
|
|
|
|
if (is_disconnected || !dependency?.reactions?.includes(derived)) {
|
|
|
|
|
(dependency.reactions ??= []).push(derived);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_disconnected) {
|
|
|
|
|
derived.f ^= DISCONNECTED;
|
|
|
|
|
}
|
|
|
|
|
// If the unowned derived is now fully connected to the graph again (it's unowned and reconnected, has a parent
|
|
|
|
|
// and the parent is not unowned), then we can mark it as connected again, removing the need for the unowned
|
|
|
|
|
// flag
|
|
|
|
|
if (is_unowned_connected && parent !== null && (parent.f & UNOWNED) === 0) {
|
|
|
|
|
derived.f ^= UNOWNED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < length; i++) {
|
|
|
|
|
dependency = dependencies[i];
|
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
|
var dependency = dependencies[i];
|
|
|
|
|
|
|
|
|
|
if (is_dirty(/** @type {Derived} */ (dependency))) {
|
|
|
|
|
update_derived(/** @type {Derived} */ (dependency));
|
|
|
|
|
@ -218,9 +177,12 @@ export function is_dirty(reaction) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unowned signals should never be marked as clean unless they
|
|
|
|
|
// are used within an active_effect without skip_reaction
|
|
|
|
|
if (!is_unowned || (active_effect !== null && !skip_reaction)) {
|
|
|
|
|
if (
|
|
|
|
|
(flags & CONNECTED) !== 0 &&
|
|
|
|
|
// During time traveling we don't want to reset the status so that
|
|
|
|
|
// traversal of the graph in the other batches still happens
|
|
|
|
|
batch_values === null
|
|
|
|
|
) {
|
|
|
|
|
set_signal_status(reaction, CLEAN);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -263,7 +225,6 @@ export function update_reaction(reaction) {
|
|
|
|
|
var previous_skipped_deps = skipped_deps;
|
|
|
|
|
var previous_untracked_writes = untracked_writes;
|
|
|
|
|
var previous_reaction = active_reaction;
|
|
|
|
|
var previous_skip_reaction = skip_reaction;
|
|
|
|
|
var previous_sources = current_sources;
|
|
|
|
|
var previous_component_context = component_context;
|
|
|
|
|
var previous_untracking = untracking;
|
|
|
|
|
@ -274,8 +235,6 @@ export function update_reaction(reaction) {
|
|
|
|
|
new_deps = /** @type {null | Value[]} */ (null);
|
|
|
|
|
skipped_deps = 0;
|
|
|
|
|
untracked_writes = null;
|
|
|
|
|
skip_reaction =
|
|
|
|
|
(flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null);
|
|
|
|
|
active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null;
|
|
|
|
|
|
|
|
|
|
current_sources = null;
|
|
|
|
|
@ -311,12 +270,7 @@ export function update_reaction(reaction) {
|
|
|
|
|
reaction.deps = deps = new_deps;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!skip_reaction ||
|
|
|
|
|
// Deriveds that already have reactions can cleanup, so we still add them as reactions
|
|
|
|
|
((flags & DERIVED) !== 0 &&
|
|
|
|
|
/** @type {import('#client').Derived} */ (reaction).reactions !== null)
|
|
|
|
|
) {
|
|
|
|
|
if (is_updating_effect && effect_tracking() && (reaction.f & CONNECTED) !== 0) {
|
|
|
|
|
for (i = skipped_deps; i < deps.length; i++) {
|
|
|
|
|
(deps[i].reactions ??= []).push(reaction);
|
|
|
|
|
}
|
|
|
|
|
@ -373,7 +327,6 @@ export function update_reaction(reaction) {
|
|
|
|
|
skipped_deps = previous_skipped_deps;
|
|
|
|
|
untracked_writes = previous_untracked_writes;
|
|
|
|
|
active_reaction = previous_reaction;
|
|
|
|
|
skip_reaction = previous_skip_reaction;
|
|
|
|
|
current_sources = previous_sources;
|
|
|
|
|
set_component_context(previous_component_context);
|
|
|
|
|
untracking = previous_untracking;
|
|
|
|
|
@ -415,9 +368,10 @@ function remove_reaction(signal, dependency) {
|
|
|
|
|
) {
|
|
|
|
|
set_signal_status(dependency, MAYBE_DIRTY);
|
|
|
|
|
// If we are working with a derived that is owned by an effect, then mark it as being
|
|
|
|
|
// disconnected.
|
|
|
|
|
if ((dependency.f & (UNOWNED | DISCONNECTED)) === 0) {
|
|
|
|
|
dependency.f ^= DISCONNECTED;
|
|
|
|
|
// disconnected and remove the mark flag, as it cannot be reliably removed otherwise
|
|
|
|
|
if ((dependency.f & CONNECTED) !== 0) {
|
|
|
|
|
dependency.f ^= CONNECTED;
|
|
|
|
|
dependency.f &= ~WAS_MARKED;
|
|
|
|
|
}
|
|
|
|
|
// Disconnect any reactions owned by this reaction
|
|
|
|
|
destroy_derived_effects(/** @type {Derived} **/ (dependency));
|
|
|
|
|
@ -564,10 +518,7 @@ export function get(signal) {
|
|
|
|
|
skipped_deps++;
|
|
|
|
|
} else if (new_deps === null) {
|
|
|
|
|
new_deps = [signal];
|
|
|
|
|
} else if (!skip_reaction || !new_deps.includes(signal)) {
|
|
|
|
|
// Normally we can push duplicated dependencies to `new_deps`, but if we're inside
|
|
|
|
|
// an unowned derived because skip_reaction is true, then we need to ensure that
|
|
|
|
|
// we don't have duplicates
|
|
|
|
|
} else if (!new_deps.includes(signal)) {
|
|
|
|
|
new_deps.push(signal);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -585,20 +536,6 @@ export function get(signal) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (
|
|
|
|
|
is_derived &&
|
|
|
|
|
/** @type {Derived} */ (signal).deps === null &&
|
|
|
|
|
/** @type {Derived} */ (signal).effects === null
|
|
|
|
|
) {
|
|
|
|
|
var derived = /** @type {Derived} */ (signal);
|
|
|
|
|
var parent = derived.parent;
|
|
|
|
|
|
|
|
|
|
if (parent !== null && (parent.f & UNOWNED) === 0) {
|
|
|
|
|
// If the derived is owned by another derived then mark it as unowned
|
|
|
|
|
// as the derived value might have been referenced in a different context
|
|
|
|
|
// since and thus its parent might not be its true owner anymore
|
|
|
|
|
derived.f ^= UNOWNED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DEV) {
|
|
|
|
|
@ -657,7 +594,7 @@ export function get(signal) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_derived) {
|
|
|
|
|
derived = /** @type {Derived} */ (signal);
|
|
|
|
|
var derived = /** @type {Derived} */ (signal);
|
|
|
|
|
|
|
|
|
|
var value = derived.v;
|
|
|
|
|
|
|
|
|
|
@ -684,9 +621,11 @@ export function get(signal) {
|
|
|
|
|
if (is_dirty(derived)) {
|
|
|
|
|
update_derived(derived);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (batch_values?.has(signal)) {
|
|
|
|
|
if (is_updating_effect && effect_tracking() && (derived.f & CONNECTED) === 0) {
|
|
|
|
|
reconnect(derived);
|
|
|
|
|
}
|
|
|
|
|
} else if (batch_values?.has(signal)) {
|
|
|
|
|
return batch_values.get(signal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -697,6 +636,25 @@ export function get(signal) {
|
|
|
|
|
return signal.v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* (Re)connect a disconnected derived, so that it is notified
|
|
|
|
|
* of changes in `mark_reactions`
|
|
|
|
|
* @param {Derived} derived
|
|
|
|
|
*/
|
|
|
|
|
function reconnect(derived) {
|
|
|
|
|
if (derived.deps === null) return;
|
|
|
|
|
|
|
|
|
|
derived.f ^= CONNECTED;
|
|
|
|
|
|
|
|
|
|
for (const dep of derived.deps) {
|
|
|
|
|
(dep.reactions ??= []).push(derived);
|
|
|
|
|
|
|
|
|
|
if ((dep.f & DERIVED) !== 0 && (dep.f & CONNECTED) === 0) {
|
|
|
|
|
reconnect(/** @type {Derived} */ (dep));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @param {Derived} derived */
|
|
|
|
|
function depends_on_old_values(derived) {
|
|
|
|
|
if (derived.v === UNINITIALIZED) return true; // we don't know, so assume the worst
|
|
|
|
|
|