chore: tweak memoizer logic

pull/17042/head
Rich Harris 7 days ago
parent 9096680c2e
commit 4dc1c6fb8e

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: tweak memoizer logic

@ -11,7 +11,7 @@ import {
import { is_ignored } from '../../../../state.js';
import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js';
import * as b from '#compiler/builders';
import { create_attribute, is_custom_element_node } from '../../../nodes.js';
import { create_attribute, ExpressionMetadata, is_custom_element_node } from '../../../nodes.js';
import { clean_nodes, determine_namespace_for_children } from '../../utils.js';
import { build_getter } from '../utils.js';
import {
@ -267,10 +267,7 @@ export function RegularElement(node, context) {
const { value, has_state } = build_attribute_value(
attribute.value,
context,
(value, metadata) =>
metadata.has_call || metadata.has_await
? context.state.memoizer.add(value, metadata.has_await)
: value
(value, metadata) => context.state.memoizer.add(value, metadata)
);
const update = build_element_attribute_update(node, node_id, name, value, attributes);
@ -487,11 +484,25 @@ function setup_select_synchronization(value_binding, context) {
);
}
/**
* @param {ExpressionMetadata} target
* @param {ExpressionMetadata} source
*/
function merge_metadata(target, source) {
target.has_assignment ||= source.has_assignment;
target.has_await ||= source.has_await;
target.has_call ||= source.has_call;
target.has_member_expression ||= source.has_member_expression;
target.has_state ||= source.has_state;
for (const r of source.references) target.references.add(r);
for (const b of source.dependencies) target.dependencies.add(b);
}
/**
* @param {AST.ClassDirective[]} class_directives
* @param {ComponentContext} context
* @param {Memoizer} memoizer
* @return {ObjectExpression | Identifier}
*/
export function build_class_directives_object(
class_directives,
@ -499,26 +510,25 @@ export function build_class_directives_object(
memoizer = context.state.memoizer
) {
let properties = [];
let has_call_or_state = false;
let has_await = false;
const metadata = new ExpressionMetadata();
for (const d of class_directives) {
merge_metadata(metadata, d.metadata.expression);
const expression = /** @type Expression */ (context.visit(d.expression));
properties.push(b.init(d.name, expression));
has_call_or_state ||= d.metadata.expression.has_call || d.metadata.expression.has_state;
has_await ||= d.metadata.expression.has_await;
}
const directives = b.object(properties);
return has_call_or_state || has_await ? memoizer.add(directives, has_await) : directives;
return memoizer.add(directives, metadata);
}
/**
* @param {AST.StyleDirective[]} style_directives
* @param {ComponentContext} context
* @param {Memoizer} memoizer
* @return {ObjectExpression | ArrayExpression | Identifier}}
*/
export function build_style_directives_object(
style_directives,
@ -528,10 +538,11 @@ export function build_style_directives_object(
const normal = b.object([]);
const important = b.object([]);
let has_call_or_state = false;
let has_await = false;
const metadata = new ExpressionMetadata();
for (const d of style_directives) {
merge_metadata(metadata, d.metadata.expression);
const expression =
d.value === true
? build_getter(b.id(d.name), context.state)
@ -539,14 +550,11 @@ export function build_style_directives_object(
const object = d.modifiers.includes('important') ? important : normal;
object.properties.push(b.init(d.name, expression));
has_call_or_state ||= d.metadata.expression.has_call || d.metadata.expression.has_state;
has_await ||= d.metadata.expression.has_await;
}
const directives = important.properties.length ? b.array([normal, important]) : normal;
return has_call_or_state || has_await ? memoizer.add(directives, has_await) : directives;
return memoizer.add(directives, metadata);
}
/**
@ -675,7 +683,7 @@ function build_element_special_value_attribute(
element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call || metadata.has_await ? state.memoizer.add(value, metadata.has_await) : value
state.memoizer.add(value, metadata)
);
const evaluated = context.state.scope.evaluate(value);

@ -26,7 +26,7 @@ export function RenderTag(node, context) {
let expression = build_expression(context, arg, metadata);
if (metadata.has_await || metadata.has_call) {
expression = b.call('$.get', memoizer.add(expression, metadata.has_await));
expression = b.call('$.get', memoizer.add(expression, metadata));
}
args.push(b.thunk(expression));

@ -35,7 +35,7 @@ export function SlotElement(node, context) {
context,
(value, metadata) =>
metadata.has_call || metadata.has_await
? b.call('$.get', memoizer.add(value, metadata.has_await))
? b.call('$.get', memoizer.add(value, metadata))
: value
);

@ -134,7 +134,7 @@ export function build_component(node, component_name, context) {
props_and_spreads.push(
b.thunk(
attribute.metadata.expression.has_await || attribute.metadata.expression.has_call
? b.call('$.get', memoizer.add(expression, attribute.metadata.expression.has_await))
? b.call('$.get', memoizer.add(expression, attribute.metadata.expression))
: expression
)
);
@ -149,7 +149,7 @@ export function build_component(node, component_name, context) {
build_attribute_value(attribute.value, context, (value, metadata) => {
// TODO put the derived in the local block
return metadata.has_call || metadata.has_await
? b.call('$.get', memoizer.add(value, metadata.has_await))
? b.call('$.get', memoizer.add(value, metadata))
: value;
}).value
)
@ -185,7 +185,7 @@ export function build_component(node, component_name, context) {
});
return should_wrap_in_derived
? b.call('$.get', memoizer.add(value, metadata.has_await))
? b.call('$.get', memoizer.add(value, metadata, true))
: value;
}
);

@ -36,7 +36,7 @@ export function build_attribute_effect(
for (const attribute of attributes) {
if (attribute.type === 'Attribute') {
const { value } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call || metadata.has_await ? memoizer.add(value, metadata.has_await) : value
memoizer.add(value, metadata)
);
if (
@ -53,9 +53,7 @@ export function build_attribute_effect(
} else {
let value = /** @type {Expression} */ (context.visit(attribute));
if (attribute.metadata.expression.has_call || attribute.metadata.expression.has_await) {
value = memoizer.add(value, attribute.metadata.expression.has_await);
}
value = memoizer.add(value, attribute.metadata.expression);
values.push(b.spread(value));
}
@ -156,9 +154,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c
value = b.call('$.clsx', value);
}
return metadata.has_call || metadata.has_await
? context.state.memoizer.add(value, metadata.has_await)
: value;
return context.state.memoizer.add(value, metadata);
});
/** @type {Identifier | undefined} */
@ -167,7 +163,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c
/** @type {ObjectExpression | Identifier | undefined} */
let prev;
/** @type {ObjectExpression | Identifier | undefined} */
/** @type {Expression | undefined} */
let next;
if (class_directives.length) {
@ -228,7 +224,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c
*/
export function build_set_style(node_id, attribute, style_directives, context) {
let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call ? context.state.memoizer.add(value, metadata.has_await) : value
context.state.memoizer.add(value, metadata)
);
/** @type {Identifier | undefined} */

@ -24,12 +24,21 @@ export class Memoizer {
/**
* @param {Expression} expression
* @param {boolean} has_await
* @param {ExpressionMetadata} metadata
* @param {boolean} memoize_if_state
*/
add(expression, has_await) {
add(expression, metadata, memoize_if_state = false) {
const should_memoize =
metadata.has_call || metadata.has_await || (memoize_if_state && metadata.has_state);
if (!should_memoize) {
// no memoization required
return expression;
}
const id = b.id('#'); // filled in later
(has_await ? this.#async : this.#sync).push({ id, expression });
(metadata.has_await ? this.#async : this.#sync).push({ id, expression });
return id;
}
@ -73,8 +82,7 @@ export function build_template_chunk(
values,
context,
state = context.state,
memoize = (value, metadata) =>
metadata.has_call || metadata.has_await ? state.memoizer.add(value, metadata.has_await) : value
memoize = (value, metadata) => state.memoizer.add(value, metadata)
) {
/** @type {Expression[]} */
const expressions = [];

Loading…
Cancel
Save