more consistent naming

blockless
Rich Harris 10 months ago
parent ba232315cf
commit a57168a367

@ -31,7 +31,7 @@ const LIS_BLOCK = -2;
/**
* @template T
* @typedef {T | import('#client').ValueSignal<T>} MaybeSignal
* @typedef {T | import('#client').Value<T>} MaybeSignal
*/
/**

@ -1,6 +1,6 @@
import { DEV } from 'esm-env';
import { CLEAN, DERIVED, UNINITIALIZED, UNOWNED } from '../constants.js';
import { current_consumer, current_effect } from '../runtime.js';
import { current_reaction, current_effect } from '../runtime.js';
import { default_equals, safe_equal } from './equality.js';
/**
@ -15,7 +15,7 @@ export function derived(fn) {
/** @type {import('#client').Derived<V>} */
const signal = {
consumers: null,
reactions: null,
deps: null,
eq: default_equals,
f: flags,
@ -31,8 +31,8 @@ export function derived(fn) {
signal.inspect = new Set();
}
if (current_consumer !== null) {
const effect = /** @type {import('#client').Effect} */ (current_consumer);
if (current_reaction !== null) {
const effect = /** @type {import('#client').Effect} */ (current_reaction);
if (effect.r === null) {
effect.r = [signal];
} else {

@ -1,7 +1,7 @@
import { DEV } from 'esm-env';
import {
current_component_context,
current_consumer,
current_reaction,
current_dependencies,
current_effect,
current_untracked_writes,
@ -34,7 +34,7 @@ export function source(value) {
f: CLEAN,
v: value,
eq: default_equals,
consumers: null,
reactions: null,
w: 0
};
@ -74,9 +74,9 @@ export function set(signal, value) {
if (
!current_untracking &&
!ignore_mutation_validation &&
current_consumer !== null &&
current_reaction !== null &&
is_runes(null) &&
(current_consumer.f & DERIVED) !== 0
(current_reaction.f & DERIVED) !== 0
) {
throw new Error(
'ERR_SVELTE_UNSAFE_MUTATION' +
@ -95,8 +95,8 @@ export function set(signal, value) {
signal.w++;
// If the current signal is running for the first time, it won't have any
// consumers as we only allocate and assign the consumers after the signal
// has fully executed. So in the case of ensuring it registers the consumer
// reactions as we only allocate and assign the reactions after the signal
// has fully executed. So in the case of ensuring it registers the reaction
// properly for itself, we need to ensure the current effect actually gets
// scheduled. i.e:
//
@ -162,7 +162,7 @@ export function mutate(source, value) {
}
/**
* @param {import('#client').ValueSignal<number>} signal
* @param {import('#client').Value<number>} signal
* @param {1 | -1} [d]
* @returns {number}
*/
@ -173,7 +173,7 @@ export function update(signal, d = 1) {
}
/**
* @param {import('#client').ValueSignal<number>} signal
* @param {import('#client').Value<number>} signal
* @param {1 | -1} [d]
* @returns {number}
*/

@ -4,13 +4,13 @@ import type { ComponentContext, EqualsFunctions, TemplateNode, Transition } from
export type EffectType = typeof EFFECT | typeof PRE_EFFECT | typeof RENDER_EFFECT;
export interface Source<V = unknown> {
/** consumers: Signals that read from the current signal */
consumers: null | Reaction[];
/** equals: For value equality */
/** Signals that read from this signal */
reactions: null | Reaction[];
/** Equality function */
eq: EqualsFunctions;
/** flags: The types that the signal represent, as a bitwise value */
/** Flags bitmask */
f: number;
/** value: The latest value for this signal */
/** The latest value for this signal */
v: V;
// write version
w: number;
@ -22,7 +22,7 @@ export interface SourceDebug<V = unknown> extends Source<V> {
export interface Derived<V = unknown> extends Source<V> {
/** dependencies: Signals that this signal reads from */
deps: null | ValueSignal[];
deps: null | Value[];
/** init: The function that we invoke for effects and computeds */
fn: () => V;
}
@ -35,7 +35,7 @@ export interface Effect {
/** context: The associated component if this signal is an effect/computed */
ctx: null | ComponentContext;
/** dependencies: Signals that this signal reads from */
deps: null | ValueSignal[];
deps: null | Value[];
/** destroy: Thing(s) that need destroying */
y: null | (() => void);
/** The types that the signal represent, as a bitwise value */
@ -62,8 +62,8 @@ export interface Effect {
export type Reaction = Derived | Effect;
export type ValueSignal<V = unknown> = Source<V> | Derived<V>;
export type Value<V = unknown> = Source<V> | Derived<V>;
export type ValueSignalDebug<V = unknown> = SourceDebug<V> | DerivedDebug<V>;
export type ValueDebug<V = unknown> = SourceDebug<V> | DerivedDebug<V>;
export type Signal = Source | Derived | Effect;

@ -51,10 +51,10 @@ let current_queued_pre_and_render_effects = [];
let current_queued_effects = [];
let flush_count = 0;
// Handle signal reactivity tree dependencies and consumer
// Handle signal reactivity tree dependencies and reactions
/** @type {null | import('#client').Reaction} */
export let current_consumer = null;
export let current_reaction = null;
/** @type {null | import('#client').Effect} */
export let current_effect = null;
@ -64,7 +64,7 @@ export function set_current_effect(effect) {
current_effect = effect;
}
/** @type {null | import('#client').ValueSignal[]} */
/** @type {null | import('#client').Value[]} */
export let current_dependencies = null;
let current_dependencies_index = 0;
@ -98,8 +98,9 @@ export function set_ignore_mutation_validation(value) {
}
// If we are working with a get() chain that has no active container,
// to prevent memory leaks, we skip adding the consumer.
let current_skip_consumer = false;
// to prevent memory leaks, we skip adding the reaction
let current_skip_reaction = false;
// Handle collecting all signals which are read during a specific time frame
export let is_signals_recorded = false;
let captured_signals = new Set();
@ -164,7 +165,7 @@ export function batch_inspect(target, prop, receiver) {
/**
* @template V
* @param {import('#client').ValueSignal<V> | import('#client').Effect} signal
* @param {import('#client').Value<V> | import('#client').Effect} signal
* @returns {boolean}
*/
function is_signal_dirty(signal) {
@ -225,24 +226,22 @@ function is_signal_dirty(signal) {
* @returns {V}
*/
function execute_reaction(signal) {
const init = signal.fn;
const flags = signal.f;
const previous_dependencies = current_dependencies;
const previous_dependencies_index = current_dependencies_index;
const previous_untracked_writes = current_untracked_writes;
const previous_consumer = current_consumer;
const previous_skip_consumer = current_skip_consumer;
const previous_reaction = current_reaction;
const previous_skip_reaction = current_skip_reaction;
const previous_untracking = current_untracking;
current_dependencies = /** @type {null | import('#client').ValueSignal[]} */ (null);
current_dependencies = /** @type {null | import('#client').Value[]} */ (null);
current_dependencies_index = 0;
current_untracked_writes = null;
current_consumer = signal;
current_skip_consumer = !is_flushing_effect && (flags & UNOWNED) !== 0;
current_reaction = signal;
current_skip_reaction = !is_flushing_effect && (signal.f & UNOWNED) !== 0;
current_untracking = false;
try {
const res = /** @type {() => V} */ (init)();
let dependencies = /** @type {import('#client').ValueSignal<unknown>[]} **/ (signal.deps);
const res = /** @type {() => V} */ (signal.fn)();
let dependencies = /** @type {import('#client').Value<unknown>[]} **/ (signal.deps);
if (current_dependencies !== null) {
let i;
if (dependencies !== null) {
@ -266,7 +265,7 @@ function execute_reaction(signal) {
? !full_current_dependencies_set.has(dependency)
: !full_current_dependencies.includes(dependency)
) {
remove_consumer(signal, dependency);
remove_reaction(signal, dependency);
}
}
}
@ -277,29 +276,29 @@ function execute_reaction(signal) {
dependencies[current_dependencies_index + i] = current_dependencies[i];
}
} else {
signal.deps = /** @type {import('#client').ValueSignal<V>[]} **/ (
signal.deps = /** @type {import('#client').Value<V>[]} **/ (
dependencies = current_dependencies
);
}
if (!current_skip_consumer) {
if (!current_skip_reaction) {
for (i = current_dependencies_index; i < dependencies.length; i++) {
const dependency = dependencies[i];
const consumers = dependency.consumers;
const reactions = dependency.reactions;
if (consumers === null) {
dependency.consumers = [signal];
} else if (consumers[consumers.length - 1] !== signal) {
if (reactions === null) {
dependency.reactions = [signal];
} else if (reactions[reactions.length - 1] !== signal) {
// TODO: should this be:
//
// } else if (!consumers.includes(signal)) {
// } else if (!reactions.includes(signal)) {
//
consumers.push(signal);
reactions.push(signal);
}
}
}
} else if (dependencies !== null && current_dependencies_index < dependencies.length) {
remove_consumers(signal, current_dependencies_index);
remove_reactions(signal, current_dependencies_index);
dependencies.length = current_dependencies_index;
}
return res;
@ -307,56 +306,59 @@ function execute_reaction(signal) {
current_dependencies = previous_dependencies;
current_dependencies_index = previous_dependencies_index;
current_untracked_writes = previous_untracked_writes;
current_consumer = previous_consumer;
current_skip_consumer = previous_skip_consumer;
current_reaction = previous_reaction;
current_skip_reaction = previous_skip_reaction;
current_untracking = previous_untracking;
}
}
/**
* @template V
* @param {import('#client').Reaction} signal
* @param {import('#client').ValueSignal<V>} dependency
* @param {import('#client').Reaction} reaction
* @param {import('#client').Value<V>} dependency
* @returns {void}
*/
function remove_consumer(signal, dependency) {
const consumers = dependency.consumers;
let consumers_length = 0;
if (consumers !== null) {
consumers_length = consumers.length - 1;
const index = consumers.indexOf(signal);
function remove_reaction(reaction, dependency) {
const reactions = dependency.reactions;
let reactions_length = 0;
if (reactions !== null) {
reactions_length = reactions.length - 1;
const index = reactions.indexOf(reaction);
if (index !== -1) {
if (consumers_length === 0) {
dependency.consumers = null;
if (reactions_length === 0) {
dependency.reactions = null;
} else {
// Swap with last element and then remove.
consumers[index] = consumers[consumers_length];
consumers.pop();
reactions[index] = reactions[reactions_length];
reactions.pop();
}
}
}
if (consumers_length === 0 && (dependency.f & UNOWNED) !== 0) {
if (reactions_length === 0 && (dependency.f & UNOWNED) !== 0) {
// If the signal is unowned then we need to make sure to change it to dirty.
set_signal_status(dependency, DIRTY);
remove_consumers(/** @type {import('#client').Derived<V>} **/ (dependency), 0);
remove_reactions(/** @type {import('#client').Derived<V>} **/ (dependency), 0);
}
}
/**
* @param {import('#client').Reaction} signal
* @param {import('#client').Reaction} reaction
* @param {number} start_index
* @returns {void}
*/
function remove_consumers(signal, start_index) {
const dependencies = signal.deps;
function remove_reactions(reaction, start_index) {
const dependencies = reaction.deps;
if (dependencies !== null) {
const active_dependencies = start_index === 0 ? null : dependencies.slice(0, start_index);
let i;
for (i = start_index; i < dependencies.length; i++) {
const dependency = dependencies[i];
// Avoid removing a consumer if we know that it is active (start_index will not be 0)
// Avoid removing a reaction if we know that it is active (start_index will not be 0)
if (active_dependencies === null || !active_dependencies.includes(dependency)) {
remove_consumer(signal, dependency);
remove_reaction(reaction, dependency);
}
}
}
@ -664,7 +666,6 @@ export async function tick() {
}
/**
* @template V
* @param {import('#client').Derived} signal
* @param {boolean} force_schedule
* @returns {void}
@ -676,7 +677,7 @@ function update_derived(signal, force_schedule) {
updating_derived = previous_updating_derived;
const status =
(current_skip_consumer || (signal.f & UNOWNED) !== 0) && signal.deps !== null
(current_skip_reaction || (signal.f & UNOWNED) !== 0) && signal.deps !== null
? MAYBE_DIRTY
: CLEAN;
@ -688,14 +689,14 @@ function update_derived(signal, force_schedule) {
// @ts-expect-error
if (DEV && signal.inspect && force_schedule) {
for (const fn of /** @type {import('#client').ValueSignalDebug} */ (signal).inspect) fn();
for (const fn of /** @type {import('#client').ValueDebug} */ (signal).inspect) fn();
}
}
}
/**
* @template V
* @param {import('#client').ValueSignal<V>} signal
* @param {import('#client').Value<V>} signal
* @returns {V}
*/
export function get(signal) {
@ -715,10 +716,10 @@ export function get(signal) {
captured_signals.add(signal);
}
// Register the dependency on the current consumer signal.
if (current_consumer !== null && (current_consumer.f & MANAGED) === 0 && !current_untracking) {
const unowned = (current_consumer.f & UNOWNED) !== 0;
const dependencies = current_consumer.deps;
// Register the dependency on the current reaction signal
if (current_reaction !== null && (current_reaction.f & MANAGED) === 0 && !current_untracking) {
const unowned = (current_reaction.f & UNOWNED) !== 0;
const dependencies = current_reaction.deps;
if (
current_dependencies === null &&
dependencies !== null &&
@ -792,44 +793,44 @@ export function invalidate_inner_signals(fn) {
}
/**
* @param {import('#client').ValueSignal} signal
* @param {import('#client').Value} signal
* @param {number} to_status
* @param {boolean} force_schedule
* @returns {void}
*/
export function mark_signal_consumers(signal, to_status, force_schedule) {
const runes = is_runes(null);
const consumers = signal.consumers;
const reactions = signal.reactions;
if (consumers !== null) {
const length = consumers.length;
if (reactions !== null) {
const length = reactions.length;
let i;
for (i = 0; i < length; i++) {
const consumer = consumers[i];
const flags = consumer.f;
const unowned = (flags & UNOWNED) !== 0;
const reaction = reactions[i];
const flags = reaction.f;
// We skip any effects that are already dirty (but not unowned). Additionally, we also
// skip if the consumer is the same as the current effect (except if we're not in runes or we
// skip if the reaction is the same as the current effect (except if we're not in runes or we
// are in force schedule mode).
if ((!force_schedule || !runes) && consumer === current_effect) {
if ((!force_schedule || !runes) && reaction === current_effect) {
continue;
}
set_signal_status(consumer, to_status);
set_signal_status(reaction, to_status);
// If the signal is not clean, then skip over it with the exception of unowned signals that
// are already maybe dirty. Unowned signals might be dirty because they are not captured as part of an
// effect.
const maybe_dirty = (flags & MAYBE_DIRTY) !== 0;
const unowned = (flags & UNOWNED) !== 0;
if ((flags & CLEAN) !== 0 || (maybe_dirty && unowned)) {
if ((consumer.f & IS_EFFECT) !== 0) {
schedule_effect(/** @type {import('#client').Effect} */ (consumer), false);
if ((flags & IS_EFFECT) !== 0) {
schedule_effect(/** @type {import('#client').Effect} */ (reaction), false);
} else {
mark_signal_consumers(
/** @type {import('#client').ValueSignal} */ (consumer),
/** @type {import('#client').Value} */ (reaction),
MAYBE_DIRTY,
force_schedule
);
@ -895,14 +896,14 @@ export function set_signal_status(signal, status) {
/**
* @template V
* @param {V | import('#client').ValueSignal<V>} val
* @returns {val is import('#client').ValueSignal<V>}
* @param {V | import('#client').Value<V>} val
* @returns {val is import('#client').Value<V>}
*/
export function is_signal(val) {
return (
typeof val === 'object' &&
val !== null &&
typeof (/** @type {import('#client').ValueSignal<V>} */ (val).f) === 'number'
typeof (/** @type {import('#client').Value<V>} */ (val).f) === 'number'
);
}
@ -1147,7 +1148,7 @@ export function inspect(get_value, inspect = console.log) {
/**
* @template V
* @param {import('#client').ValueSignal<V> | (() => V)} value
* @param {import('#client').Value<V> | (() => V)} value
* @returns {V}
*/
export function unwrap(value) {

@ -193,13 +193,13 @@ describe('signals', () => {
return () => {
$.flushSync(() => set(count, 1));
// Ensure we're not leaking consumers
assert.deepEqual(count.consumers?.length, 1);
assert.deepEqual(count.reactions?.length, 1);
$.flushSync(() => set(count, 2));
// Ensure we're not leaking consumers
assert.deepEqual(count.consumers?.length, 1);
assert.deepEqual(count.reactions?.length, 1);
$.flushSync(() => set(count, 3));
// Ensure we're not leaking consumers
assert.deepEqual(count.consumers?.length, 1);
assert.deepEqual(count.reactions?.length, 1);
assert.deepEqual(log, [0, 1, 2, 3]);
};
});
@ -259,11 +259,11 @@ describe('signals', () => {
$.flushSync(() => set(count, 4));
$.flushSync(() => set(count, 0));
// Ensure we're not leaking consumers
assert.deepEqual(count.consumers?.length, 1);
assert.deepEqual(count.reactions?.length, 1);
assert.deepEqual(log, [0, 2, 'limit', 0]);
$.destroy_signal(effect);
// Ensure we're not leaking consumers
assert.deepEqual(count.consumers, null);
assert.deepEqual(count.reactions, null);
};
});

Loading…
Cancel
Save