Skip to content

Bump Jint from 4.16.2 to 4.17.0 - #54

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/src/RoguelikeToolkit.Entities.Scripting.Jint/Jint-4.17.0
Open

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/src/RoguelikeToolkit.Entities.Scripting.Jint/Jint-4.17.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Oct 12, 2026

Copy link
Copy Markdown
Contributor

Updated Jint from 4.16.2 to 4.17.0.

Release notes

Sourced from Jint's releases.

4.17.0

Jint 4.17.0 is a release from the 4.x branch. It backports correctness and conformance fixes from main (four of them for scripts that could end the host process), linear string building, and two new opt-in APIs for hosts. That is why it is a minor release rather than a patch. Nothing in it changes an existing API or an existing default, so it is a drop-in update from 4.16.4.

Highlights

A pooled engine can let go of a finished request's objects. Hosts that reuse one engine across many evaluations (CaptureGlobalSnapshot once, RestoreGlobalSnapshot between runs) keep the interpreter's warmed caches, and that is the point of reusing it. But a warmed call site remembers the last function it called, and a warmed member read remembers the last object it read from. In a pooled web host, that can be a delegate built from a finished request's services, which then stay alive until the same script runs again. The new Engine.Advanced.DiscardInterpreterCaches() drops those caches, so the objects can be collected. The engine, its intrinsics and its global object stay as they are. The next run of each script rebuilds its cache, which costs roughly what a first run on a new engine costs, minus building the engine. A good place to call it is when an engine has been idle for a while, rather than after every use (#​4229, from #​4227).

A host can carry its own state across await. Jint now runs ECMAScript's HostMakeJobCallback and HostCallJobCallback steps for every promise reaction, every thenable job and the FinalizationRegistry cleanup callback. A host can take part through the new Options.Host.JobCallbacks, which accepts a JobCallbackHooks with Capture, Enter and Exit callbacks. That lets each branch of a Promise.all fan-out keep its own host context, such as a logging scope or ambient request data, through every await (#​4231, from #​4230; issue #​4200). The default is no hooks, in which case each promise registration costs one null check and nothing a script can observe changes.

Four more ways for a script to end the host process are closed.

  • ShadowRealm. A failure crossing a ShadowRealm boundary threw its TypeError copy from inside the catch that was handling the original failure. That added a nested exception dispatch at every hop, and no stack check could see it. Even with the stack-overflow guard on, a long chain of wrapped functions ended the process on the way back out, and a three-line script with a name getter needed no chain at all. Building that copy also ran script (toString, getters, proxy traps); it no longer does. A failed importValue now reports the import failure instead of an unrelated error (#​4220, from #​4176 and #​4183).
  • instanceof and eval. instanceof now checks the native stack before calling a custom Symbol.hasInstance method, and eval checks it before parsing. A self-referencing Symbol.hasInstance, or var s = 'eval(s)'; eval(s), now raises a RangeError the script can catch instead of ending the process. On the MaxExecutionStackCount path, eval also stopped hopping to a new thread at each overflow without ever throwing. instanceof over a bound function now consults every bound link's own Symbol.hasInstance, as the specification requires (#​4221, from #​4172, #​4184 and #​4187).
  • These checks follow 4.x's existing rule. Like every other stack check on 4.x, they are active only when Options.Constraints.StackOverflowGuard is on, so turn it on for any script you do not fully trust.

Building a string with s = s + x is linear. A + whose result reaches 512 characters now produces a deferred string instead of copying both sides. Building with s = s + x, s = x + s and s = s + a + b therefore becomes linear, as s += x already was. Short concatenations take a separate path and pay nothing for this. The deferred string is safe to read from several engines that share one Prepared<Script>, and it is charged to LimitMemory when it is built, so a memory limit still bounds it. Binding an already-bound function also no longer copies its whole "bound …" name at every level (#​4160, from #​3386, #​3571, #​4130, #​4164 and #​4173).

Array.prototype.concat spreads host arrays and lists. Since 4.14, CLR arrays and lists reach script as live views by default. concat added such a view as a single element instead of spreading its items, so a.concat(b) returned the two wrappers. It now spreads anything script sees as an array (#​4219, from #​4216; issue #​4201).

TypedArray.prototype.with survives a shrinking coercion. When converting the index or the value made a resizable buffer shorter, with copied past its new end and let a CLR ArgumentException escape. It now copies only what the buffer still holds (#​4218, from #​4157).

Verification

Every backport was verified failing-first: its tests were run against the unfixed 4.x tree on .NET 10 and .NET Framework 4.7.2, then with the change. The release candidate was measured against 4.16.4 on SunSpider and Dromaeo in six paired rounds, alternating which build ran first in each round. The four Dromaeo ObjectRegExp rows ran 12–15% faster and SunSpider's 3d-raytrace 2.3% faster. No row regressed by 1% or more; the two rows whose interval did not include zero (one Dromaeo Cube variant, +0.70%, and SunSpider's controlflow-recursive, +0.88%) are within this machine's run-to-run noise.

What's Changed

Full Changelog: sebastienros/jint@v4.16.4...v4.17.0

4.16.4

Jint 4.16.4 is a maintenance release from the 4.x branch. It backports correctness and conformance fixes from main — three of them for scripts that could end the host process — together with a few measured performance improvements, and nothing in it changes an existing API or an existing default. If you are on 4.16.3 it is a drop-in update — every public signature is the one 4.16.0 shipped, on all five target frameworks, and the per-framework snapshots in Jint.Tests.PublicInterface/Verify/ are unchanged. main remains 5.0.0 development; what is coming there is recorded as it lands in docs/v5-migration.md.

Highlights

A number reads as the same double on every target framework. Before .NET 9, the runtime's ulong-to-double and string-to-double conversions double-round, and Jint inherited that: a whole-number literal in [2⁶³, 2⁶⁴) held a different double on .NET Framework and .NET 8 than on .NET 10 (#​3531); parseFloat, Number and JSON.parse mis-rounded past their integer window on .NET Framework, and JSON.parse('1e999') threw a CLR OverflowException out of the engine instead of answering Infinity (#​3535); a fraction or exponent literal could land one ULP away on .NET Framework (#​3538); and parseInt and wide radix literals now hold the Number nearest the integer they denote on every framework, with a legacy octal no longer re-read as decimal (#​3537). The string-to-number lanes and trim now accept exactly the white space the parser does — U+0085 NEL no longer counts, and a byte-order mark no longer breaks Number or BigInt (#​3542) — and an exponent scan no longer clamps at 10⁶ (#​3593). All six arrive together as #​4120, because they share one parser. This changes numeric results — to the correct ones. The literal and string-to-number fixes change answers on .NET Framework only; parseInt, radix literals, the white-space set and the exponent clamp change them on every framework.

BigInt('+12') is 12n. The decimal spelling of a StringIntegerLiteral may carry a sign, so a leading + is accepted there and only there; BigInt('+0x10') stays a SyntaxError (#​4121).

A split keeps its segments when a constraint runs script. String.prototype.split filled a thread-shared scratch list and checks constraints every 10,000 segments; a host Constraint that ran script on the same thread could clear the list an outer split was still filling, which then returned only the segments added afterwards (#​4122).

An overload is not a fit for a number it cannot hold. Overload scoring gated float, short, byte and their kin on the value fitting, but not int and long — so 3000000000 was a perfect match for an int parameter, the wider overload beside it was never tried, and the host saw a CLR OverflowException rather than a catchable JavaScript error (#​4123).

Intl's locale lookup stops paying for a culture it does not need. BestAvailableLocale resolved a CultureInfo on every truncation step even though the available-locale set answers almost every lookup by itself; it now resolves one only when the set cannot answer (#​4124).

Intl.Locale canonicalizes every Unicode extension keyword value. UTS #​35 canonicalizes a keyword's value in two halves — the CLDR bcp47 aliases, and the removal of a value of "true" — and Jint did only the first, while Intl.Locale's own tag scanner did neither. So en-u-ca-true kept its true, en-u-kb-yes kept its yes although the data aliases it to true, and en-u-ks-primary was never aliased to level1 on the tag path. The firstDayOfWeek option was read as a Number before a String and cast to int, so 0.5 resolved to "sun" where the spec rejects it, and NaN and Infinity became whatever the framework's cast made of them — which differed between .NET and .NET Framework (#​4156).

An object used as a rotating cache stops compacting forever. A property removed from an object's store leaves a tombstone, so that a re-added key keeps its creation order. Removing the newest entry already reclaimed its slot; removing the oldest — the shape of a bounded cache, a fresh name in and the oldest out — left a hole each time, and the table compacted every capacity − live additions although the live set never grew: a 16-key rotation settled on a capacity of 64 and compacted 209 times per 10,000 steps. The entries are now a window that may wrap around the array, so a removal at either end retires its slot; the same rotation never resizes and settles on 32 (#​4155).

Chains that script can make as long as it likes no longer end the process. Resolving a property through a prototype chain recursed one native frame per link, so a 20,000-deep { __proto__: x } chain overflowed the native stack on a read, a write, an in or a with lookup and ended the process — nothing thrown, nothing for a catch to see (#​4170, from #​4078; issue #​4076, reported by @​Tielem). [[Get]], [[Set]] and [[HasProperty]] now walk the chain in a loop, so an ordinary or shaped-host-prototype chain of any depth simply answers. Function.prototype.bind chains had the same shape in IsConstructor and in the realm lookup new and Reflect.construct perform, and those are loops now too (#​4169, from #​4165); on .NET Framework the JIT happened to turn both into tail calls, so the process death was a .NET 8 / .NET 10 one.

Two chains cannot be flattened, because each link has work of its own to do on the way back out: a proxy forwarding to a proxy, and host object wrappers stacked on each other (#​4170, from #​4125; issue #​4087). Those are probed instead, and raise a catchable RangeError — when Options.Constraints.StackOverflowGuard is on. On 4.x that guard remains opt-in (it becomes the default only in 5.0), so an engine left at its defaults still ends the process on a deep enough proxy chain. If you run script you do not control, turn the guard on; with it on, a 20,000-link proxy chain used as an array's constructor or as Reflect.construct's newTarget now raises RangeError where it used to end the process.

% stops allocating for numbers. The remainder operator now takes the unboxed numeric lane multiplication and division already used, so sum += i % 97 reads a numeric counter without materialising a JsNumber per step — on the benchmark's arithmetic loop, allocation per run drops from 2.87 MB to under 1 KB (#​4167, from #​4154).

Intl.Locale.prototype.getWeekInfo reads the region the specification picks. It read CLDR's week data for the tag's literal region subtag only, so a tag without one got the world's week and the -u-rg- and -u-sd- keywords were ignored. It now follows RegionPreference: a -u-rg- override, then the region subtag, then a -u-sd- subdivision's region, then the region Add Likely Subtags supplies, then 001. This changes answers — new Intl.Locale('en').getWeekInfo().firstDay is now 7 (en is likely en-US, where the week starts on Sunday) rather than 1, and en-US-u-rg-gbzzzz answers 1 rather than 7 — in each case to what the specification and every browser give (#​4168, from #​4163).

Verification

Every backport was verified failing-first: its tests were run against the unfixed 4.x tree on .NET 10 and .NET Framework 4.7.2, then with the change.

PR tests unfixed .NET Framework unfixed .NET 10 after
#​4120 (#​3531) whole-number literals, 22 15 fail 0 fail all pass
#​4120 (#​3535) string to number, 41; JsonTests, 170 22 + 8 fail 0 fail all pass
#​4120 (#​3538) fraction / exponent literals, 50 16 fail 0 fail all pass
#​4120 (#​3537) parseInt, 124; radix literals, 21 96 + 12 fail 96 + 12 fail all pass
#​4120 (#​3542) white space, 35 22 fail 22 fail all pass
#​4120 (#​3593) exponent clamp, 5 1 fail 1 fail all pass
#​4121 StringToBigInt, 46 16 fail 16 fail all pass
#​4122 constraint re-entrancy, 2 1 fail 1 fail all pass
#​4123 numeric overload range, 20 10 fail 10 fail all pass
#​4124 culture lookup count, 8 1 fail 1 fail all pass
#​4155 creation order across rotation, 53 6 fail 6 fail all pass
#​4156 Intl.Locale canonicalization, 81 30 fail 30 fail all pass
#​4167 modulo lane, 8 (semantics only) 0 fail 0 fail all pass — the lane is proven by allocation: 2,873,104 B → 784 B per run
#​4168 getWeekInfo region preference, 26; test262 getWeekInfo region files, 8 13 + 8 fail 13 + 8 fail all pass
#​4169 bound and proxy chain walks, 3 pass (JIT tail-calls) 3 end the process all pass
#​4170 deep prototype (9), shaped-prototype (8), trapless proxy (3) and wrapper (2) chain rows, plus the PlainObject census 12 end the process, 1 fails, census fails 19 end the process, census fails all pass

Release diagnostics on the tagged commit's tree (a19802dda703), in Release: Jint.Tests 7,655 (net10.0) and 7,570 (net472); Jint.Tests.PublicInterface 1,903 and 1,895; Jint.Tests.CommonScripts 28 and 28; Jint.Tests.SourceGenerators 52; the host-contract verification leg (JINT_HOST_CONTRACT_VERIFICATION=1) 7,655 / 7,570 and 1,907 / 1,899 — zero failures anywhere, and the PublicInterface surface snapshots unchanged. test262: 102,509 passed, 0 failed, 175 skipped — the 4.x control plus the eight getWeekInfo cases #​4168 un-excluded. CI passed the same tree on Linux x64, Linux ARM64, Windows, macOS and the host-contract leg.
... (truncated)

4.16.3

Jint 4.16.3 is a maintenance release from the 4.x branch: correctness and conformance fixes backported from main, and nothing that changes an existing API or an existing default. If you are on 4.16.2 it is a drop-in update — every public signature is the one 4.16.0 shipped, on all five target frameworks, and the per-framework snapshots in Jint.Tests.PublicInterface/Verify/ are unchanged. main remains 5.0.0 development; what is coming there is recorded as it lands in docs/v5-migration.md.

Highlights

A long-lived engine stops accumulating what it has already run. Evaluate(string) and Execute(string) parse a fresh Script on every call, and the engine kept every one of them. Three of the four per-engine handler-tree caches already reset wholesale at 2048 entries so a host streaming endless distinct sources cannot grow them without bound; the fourth, _evaluatedScripts, never got that ceiling and held its keys strongly, retaining the AST of every distinct script the engine had ever evaluated — about 528 bytes per call, climbing forever and reclaimed by nothing short of dropping the engine (#​4116). The realm's tagged-template map had the same shape and a harder constraint: Realm._templateMap was a Dictionary<Node, JsArray>, strong on both ends and never cleared, costing roughly 1.35 KB per call for a frozen array and its raw array. A ceiling is no remedy there, because evicting a live template site is script-visible — f() === f() must hold for one site — so it becomes a ConditionalWeakTable<Node, WeakReference<JsArray>>, weak on both halves (#​4119). Both matter most to exactly the embedding that looks innocuous: one engine, kept for the lifetime of the process, handed ad-hoc source.

A suspended frame no longer dereferences what the suspension produced. await and yield suspend by returning a plain undefined, and the enclosing member link turns that into a sentinel reference that every consumer must recognise before reading. Nine did not, so they read undefined.undefined and raised a TypeError inside a frame that was already suspended. AsyncBlockStart swallowed that throw, but not before the statement-list resume position had been cleared on the way out — so the resume replayed the body from the first statement: one extra run of every un-awaited side effect per suspension point, and a re-entrancy guard silently truncating the rest. In a generator nothing swallows it and the TypeError comes straight out of next(). Two shapes were wrong answers rather than repeated ones — (await p).x = 1 rejected the promise, and o[await k] = 1 assigned to the literal key "undefined" instead of the real one — and for await ((await p).a of it) never terminated at all. Optional chaining was not the trigger despite where the report put it: the guarded fast lane needs a literal property name, so every computed member read of an awaited or yielded value fell through, (await p)[0] as much as (await p)[k] (#​4089, reported by @​salihvatanseverv in #​4086).

Verification

Every change was verified failing-first against the unfixed branch. The suspension fix is pinned by 35 new cases in Jint.Tests/Runtime/SuspendedOptionalChainTests.cs: against 4.16.2's code 28 fail and 6 pass on both .NET 10 and .NET Framework 4.7.2, with a 35th — the for await shape — hanging the test host outright rather than failing; after the fix all 35 pass on both. The retention fixes are pinned by Jint.Tests/Runtime/GarbageCollectionTests.cs and TaggedTemplateCacheTests.cs.

Release diagnostics on the tagged commit, in Release: Jint.Tests 7,170 (net10.0) and 7,085 (net472); Jint.Tests.PublicInterface 1,852 and 1,844; Jint.Tests.CommonScripts 28 and 28; Jint.Tests.SourceGenerators 52; the host-contract verification leg (JINT_HOST_CONTRACT_VERIFICATION=1) 7,170 / 7,085 and 1,856 / 1,848 — zero failures anywhere. test262: 102,498 passed, 183 skipped, with three files crossing the engine's default 30-second budget under whole-suite CPU contention and passing in three seconds when run alone.

The paired SunSpider and Dromaeo comparison against 4.16.2 was run after the tag rather than before it, which is a departure from how 4.16.2 was gated; it is recorded here because the result is what the release notes should carry, not the order it arrived in. No row regressed. Fifty-one rows, paired, alternating order, DefaultJob, on an idle machine: the three-round screen left two candidates clearing the sign-agreement and magnitude bar, both of them Dromaeo.StringBase64, and re-measuring those at eight rounds read −1.72% [−3.01, +1.98] and +0.30% [−3.10, +4.26] — no change, with a third parameter combination coming out faster. StringBase64 is the row Jint.Benchmark/AGENTS.md already documents as a three-round false positive, and it behaved as documented. The Cube control rows moved +0.6% to +0.8%, which is this machine's floor on rows the change cannot reach.

Nothing here is a performance change by intent. #​4119 does move a tagged-template lookup from a Dictionary to a ConditionalWeakTable and #​4089 adds suspension checks to several interpreter lanes, and neither is visible above the noise floor.

What's Changed

Full Changelog: sebastienros/jint@v4.16.2...v4.16.3

Commits viewable in compare view.

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

---
updated-dependencies:
- dependency-name: Jint
  dependency-version: 4.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added .NET Pull requests that update .NET code dependencies Pull requests that update a dependency file labels Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants