gah i give up for now. need sleep

better-effect-scheduling-ordering
Rich Harris 3 months ago
parent 6368b5da6a
commit 217210cca8

@ -206,16 +206,93 @@ export function client_component(source, analysis, options) {
}
}
for (const [node] of analysis.reactive_statements) {
const statement = [...state.legacy_reactive_statements].find(([n]) => n === node);
if (statement === undefined) {
error(node, 'INTERNAL', 'Could not find reactive statement');
const legacy_reactive_statements = Array.from(analysis.reactive_statements.keys()).map(
(node) =>
/** @type {import('./types').LegacyReactiveStatement} */ (
state.legacy_reactive_statements.get(node)
)
);
/** @type {import('#compiler').Binding[]} */
const dependencies = [];
for (const statement of legacy_reactive_statements) {
if (statement.dependencies.length === 0) {
instance.body.push(statement.body);
} else {
for (const dep of statement.dependencies) {
if (!dependencies.includes(dep)) {
dependencies.push(dep);
}
}
}
instance.body.push(statement[1]);
}
if (analysis.reactive_statements.size > 0) {
instance.body.push(b.stmt(b.call('$.legacy_pre_effect_reset')));
if (dependencies.length > 0) {
instance.body.push(b.var('$$deps', b.array(dependencies.map((dep) => b.literal(-1)))));
/** @type {import('estree').Statement[]} */
const body = [
b.stmt(
b.sequence(
dependencies.map((binding) =>
binding.kind === 'bindable_prop' || binding.kind === 'rest_prop'
? b.call(
'$.deep_read_state',
serialize_get_binding(b.id(binding.node.name), instance_state)
)
: serialize_get_binding(b.id(binding.node.name), instance_state)
)
)
)
];
const versions = dependencies.map((binding) => {
if (
binding.kind === 'state' ||
binding.kind === 'frozen_state' ||
binding.kind === 'derived'
) {
return b.member(binding.node, b.id('version'));
}
return b.call(
'$.get_version',
b.thunk(serialize_get_binding(b.id(binding.node.name), instance_state))
);
});
for (const statement of legacy_reactive_statements) {
const { dependencies } = statement;
if (dependencies.length === 0) continue;
let condition = null;
for (const binding of dependencies) {
const index = dependencies.indexOf(binding);
const version = versions[index];
const comparison = b.binary(
'!==',
version,
b.member(b.id('$$deps'), b.literal(index), true)
);
condition = condition ? b.logical('||', condition, comparison) : comparison;
}
body.push(
b.if(/** @type {import('estree').Expression} */ (condition), b.block([statement.body]))
);
}
for (let i = 0; i < dependencies.length; i += 1) {
body.push(
b.stmt(b.assignment('=', b.member(b.id('$$deps'), b.literal(i), true), versions[i]))
);
}
instance.body.push(b.stmt(b.call('$.render_effect', b.thunk(b.block(body)))));
}
/**

@ -5,10 +5,15 @@ import type {
Identifier,
PrivateIdentifier
} from 'estree';
import type { Namespace, SvelteNode, ValidatedCompileOptions } from '#compiler';
import type { Binding, Namespace, SvelteNode, ValidatedCompileOptions } from '#compiler';
import type { TransformState } from '../types.js';
import type { ComponentAnalysis } from '../../types.js';
export interface LegacyReactiveStatement {
dependencies: Binding[];
body: Statement;
}
export interface ClientTransformState extends TransformState {
readonly private_state: Map<string, StateField>;
readonly public_state: Map<string, StateField>;
@ -20,7 +25,7 @@ export interface ClientTransformState extends TransformState {
readonly in_constructor: boolean;
/** The $: calls, which will be ordered in the end */
readonly legacy_reactive_statements: Map<LabeledStatement, Statement>;
readonly legacy_reactive_statements: Map<LabeledStatement, LegacyReactiveStatement>;
}
export interface ComponentClientTransformState extends ClientTransformState {

@ -148,44 +148,19 @@ export const javascript_visitors_legacy = {
if (!reactive_stmt) return; // not the instance context
const { dependencies } = reactive_stmt;
let serialized_body = /** @type {import('estree').Statement} */ (context.visit(node.body));
if (serialized_body.type !== 'BlockStatement') {
serialized_body = b.block([serialized_body]);
}
const body = serialized_body.body;
/** @type {import('estree').Expression[]} */
const sequence = [];
for (const binding of dependencies) {
if (binding.kind === 'normal') continue;
const name = binding.node.name;
let serialized = serialize_get_binding(b.id(name), state);
// If the binding is a prop, we need to deep read it because it could be fine-grained $state
// from a runes-component, where mutations don't trigger an update on the prop as a whole.
if (name === '$$props' || name === '$$restProps' || binding.kind === 'bindable_prop') {
serialized = b.call('$.deep_read_state', serialized);
/** @type {import('#compiler').Binding[]} */
const dependencies = [];
for (const dependency of reactive_stmt.dependencies) {
if (dependency.kind !== 'normal') {
dependencies.push(dependency);
}
sequence.push(serialized);
}
// these statements will be topologically ordered later
state.legacy_reactive_statements.set(
node,
b.stmt(
b.call(
'$.legacy_pre_effect',
sequence.length > 0 ? b.thunk(b.sequence(sequence)) : b.thunk(b.block([])),
b.thunk(b.block(body))
)
)
);
state.legacy_reactive_statements.set(node, {
dependencies,
body: /** @type {import('estree').Statement} */ (context.visit(node.body))
});
return b.empty;
},

@ -135,7 +135,7 @@ var uses_version_sum = false;
export function get_version(fn) {
version_sum = 0;
uses_version_sum = true;
fn();
deep_read_state(untrack(fn));
return version_sum;
}
@ -1177,6 +1177,7 @@ export function deep_read(value, visited = new Set()) {
proto !== Date.prototype
) {
const descriptors = get_descriptors(proto);
for (let key in descriptors) {
const get = descriptors[key].get;
if (get) {

@ -1,6 +1,6 @@
export {
get,
get_version_sum,
get_version,
invalidate_inner_signals,
flushSync,
tick,

Loading…
Cancel
Save