Skip to content

Master slot memo ged#1933

Open
ged-odoo wants to merge 2 commits into
masterfrom
master-slot-memo-ged
Open

Master slot memo ged#1933
ged-odoo wants to merge 2 commits into
masterfrom
master-slot-memo-ged

Conversation

@ged-odoo

@ged-odoo ged-odoo commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

No description provided.

ged-odoo added 2 commits June 9, 2026 23:23
The template compiler stores memoization entries on props objects under
keys starting with \x01 (currently the free variables of inline arrow
props). types.strictObject reported them as unknown keys when validating
such a props object. Skip them in the unknown-key scan: they are not
user data.

This also prepares for slot memoization, which adds more synthetic keys.
Components receiving slots were re-rendered on every parent render:
slots compile to closures over the parent's render scope, rebuilt each
render, so createComponent conservatively treated their props as always
different. In slot-heavy UIs every layout component amplified renders.

The compiler now compares what slot content *captures* instead of the
closures themselves. Slot content evaluates under the child's reactive
computation, so reactive reads are already tracked by the child; `this`
and the root context are identity-stable per node. The only channels
that can change per parent render are statically enumerable:

- enclosing template variables (t-as, t-set, outer slot scopes): each
  CodeTarget collects the ctx keys its code reads (handler expressions
  are collected separately since they hoist to staticDefs), nested
  targets bubble up, and each capture is emitted as a synthetic
  "\x01slots.<var>": ctx['<var>'] prop compared by the existing
  propList machinery
- slot params: hoisted and compared by value; .bind params compare the
  unbound function so a stable method keeps the memo
- forwarded slots (static t-call-slot): captured by identity of the
  defining component's incoming slot object, so wrapper components
  memoize through the chain

When captures cannot be enumerated, the call site falls back to always
re-rendering (the previous behavior): t-call in slot content, t-out="0",
dynamic t-call-slot names, and captures t-set after the call site or
reassigned across loop iterations (the synthetic would read a different
value than the lazily evaluated slot content sees); the last rule is
resolved by a post-compilation pass once write order is known.

The 4th createComponent argument now means "has opaque slots" instead
of "has slots".

Semantic change: slot content reading non-reactive values is no longer
refreshed as a side effect of unrelated parent renders (render reads
reactive state, inside slots too). Deep renders still bypass
memoization. Pinned in slots_memoization.test.ts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant