Default Interop.CacheRecentObjectWrappers to true for 4.14#2734
Merged
Conversation
Closes sebastienros#2729. With LiveView as the array default, wrapper re-creation per crossing was the dominant remaining interop cost; the bounded recent-wrapper ring (8 entries, per engine) makes repeated crossings of the same object reuse its wrapper by default while staying memory-safe. Flipping the default surfaced a latent cache bug that also affected the opt-in flags: lookups ignored the exposed CLR type, so an object crossing both as its concrete type and as an explicit interface/superclass view got the wrong wrapper back. Cache hits now require the wrapper's ClrType to match the exposed type; the ring can hold both views of one object, and the identity map replaces its entry when the view changes. Copy-mode tests that assert fresh-snapshot-per-crossing semantics opt out of the cache explicitly, mirroring the sebastienros#2728 migration; the option XML doc spells out how to restore the full pre-4.14 behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lahma
enabled auto-merge (squash)
July 22, 2026 08:34
This was referenced Jul 22, 2026
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.
Closes #2729.
With
LiveViewas the array default (#2728) and the wrapper-construction costs removed (#2730, #2731), the dominant remaining cost of a collection member being re-read from script was wrapper re-creation per crossing. This flipsOptions.Interop.CacheRecentObjectWrapperstotrue: a small bounded ring (8 entries, per engine, strong references — cannot grow unbounded) makes repeated crossings of the same object reuse its wrapper by default.TrackObjectWrapperIdentityremains the opt-in for full identity persistence; settingCacheRecentObjectWrappers = falserestores fresh-wrapper-per-crossing.Latent cache bug fixed along the way (it also affects the existing opt-in flags): both identity-cache lookups ignored the exposed CLR type, so an object crossing once as its concrete type and later as an explicit interface/superclass view (
holder.i1vsholder.ci1) got the first wrapper back — resolving the wrong member set. Seven existingInteropExplicitTypeTestscaught this the moment the cache was on by default. Cache hits now require the wrapper'sClrTypeto match the exposed type; the ring can hold both views of the same object, and the identity map replaces its entry when the view changes. A regression test exercises alternating views against the populated cache.Observable changes (and their opt-outs):
Object.freeze,defineProperty, expandos) survives crossings. (===was already true — wrapper equality compares targets.)ArrayConversionMode.Copy, repeated reads of the same CLR array now reuse the first JsArray snapshot while cached — CLR-side mutations are not re-copied. Full pre-4.14 semantics =Copy+CacheRecentObjectWrappers = false; the option XML doc spells this out and the snapshot-contract tests migrated to the explicit opt-out, mirroring the Default Interop.ArrayConversion to LiveView for 4.14 #2728 test migration.Benchmarks (same machine, adjacent same-base A/B vs main@fed894d43, default job)
InteropWrapperChurnBenchmark:EngineComparisonInteropBenchmark(Jint lane):Cumulative on this row across the 4.14 interop work (#2728 → #2730/#2731 → this PR): 15.6 ms / 32.9 MB → 1.50 ms / 0.33 MB.
Gates: full Jint.Tests (net10.0 + net472), Jint.Tests.PublicInterface, Test262 99,431 passed / 0 failed.
🤖 Generated with Claude Code