Skip to content

Extend prepared-script engine-retention test to newer env cache shapes#2594

Merged
lahma merged 2 commits into
sebastienros:mainfrom
lahma:additional-memory
Jul 6, 2026
Merged

Extend prepared-script engine-retention test to newer env cache shapes#2594
lahma merged 2 commits into
sebastienros:mainfrom
lahma:additional-memory

Conversation

@lahma

@lahma lahma commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to the analysis of #2587.

Analysis summary

Walking every object a prepared script can retain after engine disposal (everything parked on AST UserData at prepare/build time) confirms the v4.11.0 snapshot in #2587 is fully by design — nothing engine-owned survives:

Retained type What it is Why it is safe
JintConstantExpression + JsString/JsNumber constant-folding results for literals and foldable unary/binary expressions immutable, realm-free primitives
JintFunctionDefinition.State per-function FDI metadata (parameter/slot layout, hoisting); grew 56→92 B for the fixed-slot call fast paths the only environment field (_dynamicCachedEnv) is gated to Function-constructor definitions living in the per-realm cache, never on prepared ASTs; _cachedSlots is cleared before parking
JintBlockStatement.BlockState block declaration cache (wraps the former DeclarationCache, 16→28 B) documented invariant: never holds environments — env caches live on per-engine handler instances
Environment.BindingName Key + canonical JsString per identifier name CalculatedValue is readonly and only ever the Undefined singleton
CachedHoistingScope, ConstantStatement hoisting cache / stateless return-literal handler no engine state

The net retained size actually dropped vs v4.4.0 (127.8 → 120.7 MB) because identifiers now park a single deduplicated BindingName per name instead of one JintIdentifierExpression handler per node (was ~8 MB in the v4.4.0 snapshot).

Change

GarbageCollectionTests.SharedPreparedScriptDoesNotRetainEngines locks the invariant that a shared prepared script must not pin engines, but it predates two environment-cache shapes added since it was written:

This extends the test script to exercise both, so a future refactor moving either cache onto shared AST state fails the test. Verified green on net10.0 and net472.

No product code changes; #2587 needs no fix.

🤖 Generated with Claude Code

Analysis of sebastienros#2587 confirmed that everything a prepared script retains
after engine disposal is engine-independent prepare-time metadata by
design (constant-folded primitives, FDI State, block declaration
caches, binding names) and that no Engine/Realm/Environment can be
reached from the shared AST.

The regression test guarding that invariant predates two environment
cache shapes added since: the for-of/for-in per-iteration environment
cache on the JintForInForOfStatement handler (sebastienros#2586) and the
Function-constructor definition-level environment parked on the
realm-cached dynamic State (sebastienros#2579). Cover both so a future refactor
moving either onto shared AST state fails the test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lahma
lahma enabled auto-merge (squash) July 6, 2026 21:27
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