Repository navigation
Backport #4230 to 4.x: Let a host carry its own state across await through HostMakeJobCallback - #4231
Merged
Conversation
…oss await through HostMakeJobCallback Adapted from ffd2651 (and d2dee6f, whose security-posture classification has no 4.x counterpart). ECMA-262's HostMakeJobCallback / HostCallJobCallback now run for every promise reaction, every thenable job and FinalizationRegistry's [[CleanupCallback]], and a host can take part through the new, purely additive Options.Host.JobCallbacks (a Jint.Runtime.JobCallbackHooks with Capture / Enter / Exit). With no hooks installed nothing changes: each registration pays one null test and the reaction record is untouched. Adapted for 4.x: HostOptions.JobCallbacks is a plain { get; set; } read once at engine construction (4.x options are not frozen), the primitive-await lane wraps its continuation without main's memory-limit state, the tests are transcribed to xUnit against Engine.Advanced, main's frozen-options test is replaced by one pinning the read-once behaviour, and the docs go to README.md and AGENTS.md instead of the docs site and the co-located AGENTS files. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
lahma
force-pushed
the
backport/4x-job-callback-hooks
branch
from
October 6, 2026 21:16
7aa307b to
d162a2f
Compare
This was referenced Oct 7, 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.
Backports #4230 to 4.x. Jint now runs ECMA-262's HostMakeJobCallback and HostCallJobCallback for every promise reaction, every thenable job and the FinalizationRegistry cleanup callback, and a host can join in through a new opt-in API:
Options.Host.JobCallbackstakes aJobCallbackHookswithCapture,EnterandExit. This covers the embedder scenario in #4200: each branch of aPromise.allfan-out keeps its own host flow across everyawait.The change is purely additive. With no hooks installed, which is the default, each promise registration pays one null test, the reaction record is unchanged, and nothing script can observe changes. That is why it can go to 4.x despite the branch's no-new-API rule.
JobCallbackHooksandOptions.Host.JobCallbacks, wired into bothPerformPromiseThenoverloads, the primitive-awaitshortcut,NewPromiseResolveThenableJoband FinalizationRegistry cleanup.Adapted for 4.x
The public API matches main exactly: the same namespace, names, signatures and
protected internalmembers. Code written against 4.x will compile unchanged on v5. Only the internals and the tests were adapted.HostOptions.JobCallbacksis a plain{ get; set; }with noThrowIfReadOnly. The engine reads it once, at construction, as main does afterOptions.Apply. The XML doc says so.TheHooksAreFrozenWithTheRestOfTheOptionstest does not apply to 4.x. It is replaced byTheHooksAreReadOnceWhenTheEngineIsConstructed: setting the property on a live engine's options installs nothing.OptionsSecurityPostureTestslist. 4.x has neither that list norForUntrustedCode, so there is nothing to carry over.PromiseReactionhas no memory-limit state, so the primitive-awaitlane wraps its continuation throughPromiseOperations.MakeJobCallbackwithout main'sCaptureMemoryLimitState()argument. Every other engine hunk applied cleanly. I compared thenew PromiseReaction(sites on both branches: 4.x has no extra reaction lane that would need wrapping.[Fact]) andEngine.Advanced.ProcessTasks/RegisterPromise. The harness resolves with aJsValue, because 4.x'sManualPromise.Resolvetakes one.PerformPromiseThenstill owes its HostMakeJobCallback" gotcha go into the root AGENTS.md. Main'sSchedulerQueueremark is about Jint/WebApi, which 4.x does not have.Public API additions
Each of the five Verify snapshots (net10.0, net8.0, net462, netstandard2.0, netstandard2.1) gains exactly these 8 lines, byte-identical to main's additions:
Evidence
The new tests cannot compile against 4.x without the public type. So, as on main, "unfixed" means the API is present but the engine never takes the hooks from Options. Under that,
JobCallbackHooksTestsplus the FinalizationRegistry test fail 10 of 14 on both net10.0 and net472, andHostJobCallbackTestsfails 2 of 3 on both, including the fan-out scenario. The tests that still pass are the ones asserting nothing is carried or that Enter/Exit stay balanced.I also removed each wiring site in turn and re-ran the new tests on net10.0. Every site has at least one test that fails, the same as on main:
awaitlane: the await test and the PublicInterface fan-out fail.CallJobCallback: the cleanup test fails.thenhandlers: 4 Jint.Tests and 2 PublicInterface tests fail.Exitoutside afinally: the throwing-handler test fails.With the change in place, all of them pass on both frameworks.
To check the code path that captures, I ran a local, uncommitted build that forces an always-capturing no-op hook onto every engine. Jint.Tests net10.0 passed 7,802 of 7,802 non-skipped. A broader test262 async subset (adding for-await-of, async arrows, async generators and AsyncFromSyncIteratorPrototype to the filter below) passed 6,280 of 6,280.
Totals
JINT_HOST_CONTRACT_VERIFICATION=1, net10.0): Jint.Tests 7,802 passed; PublicInterface 1,954 passed; 0 failed in both.Performance
No benchmarks were run. The same rows that gate #4230 should gate this:
🤖 Generated with Claude Code