Class RenderDepthGuard
Five recursions walk the Element tree and need this guard:
Element.mount-- the mounting recursion itself (CR-01): everymount(Element)override callssuper.mount(parent)before mounting its own children, so checking here guardsContainerElement/GridViewElement.mountChildren()and themount()->performRebuild()-> updateChild()->mount()cascadeStatelessElement/StatefulElementdrive, in one place. This is the recursion that runs FIRST -- before any of the four below -- both on the very first frame and whenever a later rebuild introduces a brand-new subtree.GuiRenderer.rebuildElement-- the element-rebuild recursion, against an already-mounted treeGridViewElement.performRebuild-- nested inside the above for a GridView's own childrenGridViewElement.collectRenderNodeLeaves-- walks a GridView cell's subtree to propagate its computed slot to every leafGuiRenderer.collectRenderNodesRecursive-- the render-node collection recursion, last in the frame
Element.update()/Element.unmount() need no separate guard: both only ever walk
a subtree whose depth was already validated by the mount guard when it was first mounted, so
neither can exceed MAX_DEPTH without first passing through an unguarded
mount() call -- which the guard above closes. Navigator.of() walks the tree
UPWARD via a plain loop over Element.getParent() (the same iterative shape as
depthOf(Element) itself) -- not recursion, so it needs no guard either.
A module-authored widget tree deep enough overflows the JVM stack mid-frame;
StackOverflowError is an Error, so nothing on the render path catches it and it
escapes the scheduled frame, potentially leaving the Inventory half-written. Each of the five
call sites checks its current depth against MAX_DEPTH before descending further,
raising RenderDepthExceededException -- a named, attributable failure -- instead.
Derivation of MAX_DEPTH. Every widget tree shipped in this codebase -- the
ShopPage example in the framework's own documentation, and every fixture across
GridViewTest/ContainerTest/ElementReconciliationTest -- nests at most
3-4 real levels deep: a root Container wrapping a GridView or a second
Container, wrapping a leaf ItemDisplay/TextButton, occasionally with one
extra StatefulWidget/StatelessWidget composition layer. MAX_DEPTH is set
to 64: roughly 16x that observed depth, generous headroom for legitimate composition this
codebase does not currently exercise (nested Navigator routes, deeply composed stateful
widgets), while remaining far below the point where these lightweight, few-local-variable
recursive methods would exhaust the JVM's default thread stack (in practice, comfortably in the
thousands of frames for a call shape this simple, on the stack sizes Paper's main thread runs
with). It is a sanity ceiling on module-authored tree shape, not an attempt to find the exact
point of stack exhaustion -- the guard is meant to trip long before the JVM ever would.
WR-03: @ApiStatus.Internal -- purely internal render-engine machinery; no
module-author use case for catching RenderDepthExceededException or referencing
MAX_DEPTH directly is intended, and this release adds no new public surface.
- Since:
- 6.3.0
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intThe maximum Element-tree depth any of the four render-frame recursions may reach beforecheck(String, int)raisesRenderDepthExceededException. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic voidstatic intComputes an Element's depth by walking itsElement.getParent()chain -- an iterative loop, not recursion, so computing the depth itself can never contribute to the stack exhaustion this guard exists to prevent.
-
Field Details
-
MAX_DEPTH
public static final int MAX_DEPTHThe maximum Element-tree depth any of the four render-frame recursions may reach beforecheck(String, int)raisesRenderDepthExceededException. See the class javadoc for the derivation.- See Also:
-
-
Constructor Details
-
RenderDepthGuard
private RenderDepthGuard()
-
-
Method Details
-
check
RaisesRenderDepthExceededExceptionifdepthexceedsMAX_DEPTH. A tree exactly at the limit is allowed through -- only depths strictly greater trip the guard.- Parameters:
guardName- a name identifying which of the four recursions is checking, surfaced in the exception message so a trip is attributabledepth- the current Element-tree depth, as computed bydepthOf(Element)
-
depthOf
Computes an Element's depth by walking itsElement.getParent()chain -- an iterative loop, not recursion, so computing the depth itself can never contribute to the stack exhaustion this guard exists to prevent. The mounted root (no parent) is depth 0.- Parameters:
element- the Element whose depth to compute- Returns:
- the number of ancestors between
elementand the mounted root
-