[SPIKE] Replace DynamicScope with a render scope tracker#21487
Closed
NullVoxPopuli-ai-agent wants to merge 2 commits into
Closed
[SPIKE] Replace DynamicScope with a render scope tracker#21487NullVoxPopuli-ai-agent wants to merge 2 commits into
NullVoxPopuli-ai-agent wants to merge 2 commits into
Conversation
Removes the template-facing dynamic vars feature: - the -get-dynamic-var (call/append) and -with-dynamic-vars (block) keywords, the GetDynamicVar/WithDynamicVars MIR nodes, their encoding and strict-mode passes - wire format opcodes WithDynamicVars (45) and GetDynamicVar (53) and their types (numbers left as gaps, not renumbered) - VM opcodes GetDynamicVar (111) and BindDynamicScope (58), their append-opcode handlers, VM#bindDynamicScope, and debug metadata - the associated integration test suites Dynamic scope itself stays: Ember's outlet machinery reads and writes outletState on the dynamic scope directly from JS, and component invocation still pushes/pops dynamic scope. Only the template syntax for reading/writing arbitrary dynamic vars is removed. Note: this deletes the intimate API liquid-fire uses to intercept outletState in templates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removes the VM's DynamicScope concept (interface, impl, per-closure
capture, manager/helper parameters) and replaces it with a minimal
render scope tracker: a parent-linked node tree where components with
the renamed renderScope capability provide ambient values on their own
node and descendants read the nearest provided value.
Because provides land on the provider's own node, sibling isolation is
free, and custom-manager components no longer need a scope frame at all
(they only had dynamicScope: true to prevent set() leaking to
siblings). Update-path correctness — previously handled by capturing
dynamicScope in every block closure — is handled by paired
Enter/ExitRenderScopeOpcode updating opcodes that re-establish the
current node as the updating VM descends.
Ember's two ambient values migrate to private symbol keys in
utils/render-scope.ts: the outlet component manager provides
OUTLET_STATE and the {{outlet}} helper reads the nearest one; curly and
root managers provide/read VIEW for the classic parentView chain.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Spike: remove the VM's
DynamicScopeconcept entirely, replacing it with a minimalrender scope tracker that carries Ember's two ambient values (
outletStateand theclassic-component
viewchain).Stacked on #21485 (its commit is included here): that PR removes the template-facing
dynamic vars (
-with-dynamic-vars/-get-dynamic-var); this one removes the machineryunderneath. Related to (but not dependent on) the closed #21450: the tracker here is the
internal core of what that PR's glimmer side did — the render-tree-positional
provide/lookup mechanism — without any public
createContextAPI. If a Context featurelands later, it can grow out of this same mechanism instead of a parallel one.
The replacement mechanism
RenderScopeStackImpl(@glimmer/runtime/lib/render-scope.ts): a parent-linked treewith one node per component that has the (renamed)
renderScopecapability. A componentprovides values on its own node during
create; descendants read the nearestprovided value by walking the parent chain.
Two properties make this a faithful replacement for dynamic scope:
child scope around every component that might contain a mutating descendant, so
custom-manager (Glimmer) components all carried
dynamicScope: truepurely to stopsetfrom leaking to siblings. With provides landing on the provider's own node,nothing can leak, and custom-manager components flip to
renderScope: false— fewerscope frames than before, not more.
VM_PUSH_RENDER_SCOPE_OP/VM_POP_RENDER_SCOPE_OP; on the append pass they push/popnodes and install
EnterRenderScopeOpcode/ExitRenderScopeOpcodein the updatingopcode list. The updating VM therefore re-establishes the correct node as it descends,
so content re-rendered mid-update (a
TryOpcodere-rendering after a route change,an
{{#if}}flipping around an{{outlet}}) sees the same ambient state it would oninitial render. This is the job closure capture of
dynamicScopeused to do.Removed
DynamicScopeinterface (get/set/child),DynamicScopeImplpushDynamicScope/popDynamicScope/dynamicScope(),and the
dynamicScopefield captured in every block closure (ClosureState/VMState)dynamicScopeparameter threaded throughrenderMain/renderComponent,WithCreateInstance#create,WithUpdateHook#update, and the internal helper signatureDynamicScopeclass inrenderer.ts(and itsoutletState-only assertions)fromDynamicScopetest-infra feature inemberish-curlyRenamed / migrated
dynamicScope→renderScope(same bit); ops 59/60 renamed as above@ember/-internals/glimmer/lib/utils/render-scope.tsas private symbols: the outlet component manager provides
OUTLET_STATE; the{{outlet}}helper reads the nearest one (internal helpers now receive the currentRenderScopeNodeinstead of aDynamicScope); curly/root managers provide/readVIEWfor the classic
parentViewchainTesting
pnpm type-check:internals— clean (the two@glimmer/componenterrors in ownertype-tests are pre-existing on
main)pnpm lint:eslint— clean(identical tally to
main+ [SPIKE] Remove -with-dynamic-vars / -get-dynamic-var support #21485, so outlet nesting, engines, and the classicparentViewchain are all exercised and passing)grep -ri dynamicscope packages/— zero remaining references🤖 Generated with Claude Code