Skip to content

Generators: a yield* delegation both re-delegates and keeps its place (backport of #3506 and #3518) - #3545

Merged
lahma merged 2 commits into
sebastienros:4.xfrom
lahma:backport/3506-3518-generator-fixes
Sep 1, 2026
Merged

lahma merged 2 commits into
sebastienros:4.xfrom
lahma:backport/3506-3518-generator-fixes

Conversation

@lahma

@lahma lahma commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Backports #3506 and #3518 to 4.x. Both are silent-wrong-result defects in generator
resumption; together they are 16 lines of production code across four files.

Neither is theoretical on this branch. The ten tests the two PRs added were ported onto unmodified 4.x first, and
every one of them fails there — counts below.

What is wrong on 4.x today

#3506 — the yield memo is never invalidated. Jint resumes a generator by replaying its body from the top and
skipping the work earlier passes already did, using a memo of what each yield node last returned. Nothing ever removed
an entry, so the second time a loop reached the same yield node the memo answered it with the first iteration's
value — without evaluating its operand at all. For yield (yield* g()) that abandons the new delegation before it
starts, and when the operand carries the loop's own decrement the loop never ends:

function* countdown(n) {
    while (n > 0) {
        yield (yield* countdown(--n));   // the operand carries the decrement
    }
    return 34;
}
collect(countdown(3));   // 4.x: never returns from the sixth next()

That is staging/sm/generators/delegating-yield-9.js, which this branch removes from the exclusion list. Move the
decrement into its own statement and the loop terminates but answers with six results instead of eight — the silent half
of the same defect, which is why it gets its own test.

#3518 — a yield* delegation publishes no suspension node. Each statement on the way back into a replayed body has
to recognise it is being re-entered rather than entered, or a while re-runs the test that already chose the iteration
it is inside. JintWhileStatement, JintForStatement, JintDoWhileStatement, JintIfStatement, JintSwitchStatement
and JintTryStatement all ask JintStatement.GetSuspensionNode, which reads exactly one property:
ISuspendable.LastSuspensionNode. A delegation parks its suspension point in _delegatingYieldNode — necessarily a
separate field, because JintYieldExpression selects its delegation branch on that one and its plain-yield branch on
_lastYieldNode — and nothing published it. Every resume taken with a delegation in flight therefore looked to all six
statements like a resume that had suspended nowhere, and each re-ran its test against state the delegated code had
already moved.

The synchronous half needs no recursion to show it:

function* leaf() { yield 1; }
function* outer() {
    var log = [];
    var n = 2;
    while (n > 0) {
        n = 0;
        yield* leaf();
        log.push('after');
    }
    return log.join(',');
}
// node: 1, then 'after'.  4.x: 1, then ''.

The fix

One Remove on the fresh-evaluation path of JintYieldExpression, and one ?? _delegatingYieldNode fallback on each of
the two instance types:

object? ISuspendable.LastSuspensionNode => _lastYieldNode ?? _delegatingYieldNode;

It cannot answer with a stale node: _lastYieldNode is cleared the moment the yield it names is resumed, so it is null
for as long as a delegation is in flight, and _delegatingYieldNode is cleared when the delegation ends.

Per 14.4.14 every evaluation
of yield * AssignmentExpression evaluates its operand and drives the resulting iterator, and
AsyncGeneratorYield suspends the generator at the yield; neither
re-evaluates the iteration statement the yield* sits inside.

All four production hunks are byte-identical to the ones merged on main. JintYieldExpression.cs,
GeneratorInstance.cs and ISuspendable.cs are in fact the same blob as on main after the pick;
AsyncGeneratorInstance.cs differs only in pre-existing 4.x/main drift elsewhere in the file (two spec anchors in
unrelated <summary> comments), never in the changed lines.

Evidence: the ten tests on unmodified 4.x

Ported onto e556d274 with no production change, in Jint.Tests/Runtime/GeneratorTests.cs:

before after
net472 Failed: 11, Passed: 40, Total: 51 Failed: 0, Passed: 50, Total: 50
net10.0 Failed: 12, Passed: 40, Total: 52 Failed: 0, Passed: 50, Total: 50

Ten distinct test methods fail on both; the runner emits 10–12 records for them from run to run, because the cases
that do not terminate at all are recorded a second time when their ten-second timeout fires. Repeated before runs gave
10, 11 and 12 — the distinct set of ten never varied.

(Jint.Tests targets net472 and net10.0 on this branch. net462 is the Jint library's floor, not a test leg, so
the interpreter change is compiled for net462 by dotnet build and exercised on net472.)

As the runner reported them on unmodified 4.x — which is what "silently wrong" means in practice: a shorter sequence,
or the other branch, never an exception:

Failed ADelegatingYieldInsideAYieldKeepsItsPlaceWhenTheDecrementIsElsewhere
  Expected engine.Evaluate(Script) to be
    "34:false 34:false 34:false 34:false 34:false 34:false 34:false 34:true (String)",
  but found
    "34:false 34:false 34:false 34:false 34:false 34:true (String)".      6 results, not 8

Failed AnAsyncDelegatingYieldInsideAYieldKeepsItsPlaceWhenTheDecrementIsElsewhere
  "…34:false 34:true"                                       (actual)      4 results, not 8
  "…34:false 34:false 34:false 34:false 34:false 34:false…"  (expected)

Failed ADelegatingYieldSuspensionKeepsTheEnclosingWhileLoopFromRestarting
Failed AnAsyncDelegatingYieldSuspensionKeepsTheEnclosingWhileLoopFromRestarting
  "1:false :true"                                           (actual)      'after' never ran
  "1:false after:true"                                      (expected)

Failed ADelegatingYieldSuspensionKeepsTheEnclosingIfFromTakingTheOtherBranch
Failed AnAsyncDelegatingYieldSuspensionKeepsTheEnclosingIfFromTakingTheOtherBranch
  "1:false else:true"                                       (actual)      wrong branch on resume
  "1:false then:true"                                       (expected)

Failed ADelegatingYieldSuspensionKeepsTheEnclosingForLoopFromRestarting
Failed AnAsyncDelegatingYieldSuspensionKeepsTheEnclosingForLoopFromRestarting
  "1:false 1:false 1:false 1:false 1:false 1:false 1:false…" (actual)     only the guard ends it
  "1:false body:true"                                       (expected)

Failed ADelegatingYieldInsideAYieldStartsOverOnEveryLoopIteration           does not terminate (10 s)
Failed AnAsyncDelegatingYieldInsideAYieldStartsOverOnEveryLoopIteration     does not terminate (10 s)

Note the last pair: on 4.x the async recursive shape does not return either, where on main (with #3506 already
in) it returned four results. #3518's write-up says as much — "on 4.16.1 the same script does not return at all" — and
that is confirmed here. The two fixes are needed together on this branch.

Each test asserts the exact sequence of value:done pairs rather than a count, and drains through a twenty-result guard
so a regression that never terminates fails with a wrong string instead of hanging the run.

Differences from the two PRs on main

Faithful except where 4.x has no equivalent:

  • Jint.Tests is xUnit here, NUnit on main. [Test, CancelAfter(10000)][Fact(Timeout = 10000)],
    TestContext.CurrentContext.CancellationTokenTestContext.Current.CancellationToken, and
    Options.ObserveCancellationOptions.CancellationToken. The token is threaded through the Drain helper as a
    parameter rather than read inside it, because xUnit's xUnit1069 analyzer requires the test method itself to
    reference TestContext.Current.CancellationToken for a Timeout to be able to bite.
  • No docs/v5-migration.md on this branch, so both migration-guide hunks are dropped.
  • No Jint/Runtime/Interpreter/AGENTS.md on this branch4.x has a single root AGENTS.md and no co-located
    ones — so Generators: a yield* delegation keeps its place in the statement around it (#3509) #3518's gotcha entry is dropped with it.

Production code and the test bodies themselves are otherwise unchanged.

Test runs

dotnet build -c Release clean, dotnet test -c Release green:

project net472 net10.0
Jint.Tests 6758 passed / 0 failed / 4 skipped 6843 passed / 0 failed / 4 skipped
Jint.Tests.PublicInterface 1493 / 0 / 9 1500 / 0 / 9
Jint.Tests.CommonScripts 28 / 0 / 0 28 / 0 / 0
Jint.Tests.SourceGenerators 52 / 0 / 0

test262

Both runs in isolation on net10.0, same machine, nothing else running:

passed failed skipped total
before (e556d274) 102,495 0 189 102,684
after 102,497 0 187 102,684

Exactly +2 passed / −2 skipped: the two modes of staging/sm/generators/delegating-yield-9.js, which this branch removes
from the exclusion list, and nothing else. No other exclusion is touched.


Original PRs: #3506 and #3518 (the latter fixes #3509).

🤖 Generated with Claude Code

https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S

lahma and others added 2 commits September 1, 2026 05:14
…tienros#3506)

Backport of sebastienros#3506 to 4.x.

Jint resumes a generator by replaying its body from the top and skipping the
work earlier passes already did. Part of that bookkeeping is a memo of what
each yield node last returned, so a statement re-executed after a later
suspension does not re-run the yields it already got past.

Nothing invalidated an entry. The second time a loop reached the same yield
node the memo answered it with the first iteration's value, without evaluating
its operand at all -- so `yield (yield* g())` abandoned the new delegation
before it started, and when the operand carried the loop's own decrement the
loop never ended. That is staging/sm/generators/delegating-yield-9.js: one
next() that never comes back on an input of three.

Reaching the normal-yield path is a fresh evaluation of that node, so the value
its previous evaluation produced stops being an answer to it. Per 14.4.14 every
evaluation of `yield * AssignmentExpression` evaluates its operand and drives
the resulting iterator, so a node a loop returns to starts a delegation of its
own each time. countdown(3) now reports eight results, as SpiderMonkey and V8
do.

The production change is byte-identical to the one on main. The tests are the
same two cases rewritten for 4.x's xUnit test project.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S
…nd it (sebastienros#3518)

Backport of sebastienros#3518 to 4.x. Fixes sebastienros#3509 there.

An engine that resumes a generator by replaying its body has to tell each
statement it is being re-entered rather than entered, or a `while` re-runs the
test that already chose the iteration it is inside. The node the generator
suspended at is what said so, and a `yield*` delegation kept its suspension
point in `_delegatingYieldNode` while only `_lastYieldNode` was published as
`ISuspendable.LastSuspensionNode`. Every resume with a delegation in flight
therefore looked like a resume that had suspended nowhere.

Publish the delegation node as the fallback. It cannot be stale:
`_lastYieldNode` is cleared the moment the yield it names is resumed, so it is
null for as long as a delegation runs, and `_delegatingYieldNode` is cleared
when the delegation ends.

Async generators pay it twice, because a delegation that completes also resumes
through a replay -- the inner step settles on a later microtask -- which is why
the recursive `yield (yield* countdown(--n))` of sebastienros#3509 reported four results
where V8 and SpiderMonkey report eight.

The production change is byte-identical to the one on main. The tests are the
same eight cases rewritten for 4.x's xUnit test project; 4.x has no
Jint/Runtime/Interpreter/AGENTS.md and no docs/, so those two hunks are dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S
@lahma
lahma merged commit a6eb1b5 into sebastienros:4.x Sep 1, 2026
9 of 10 checks passed
PatrickSt1991 pushed a commit to Apps2Samsung/Apps2Samsung that referenced this pull request Sep 14, 2026
Updated [Avalonia](https://github.com/AvaloniaUI/Avalonia/) from 11.3.20
to 11.3.22.

<details>
<summary>Release notes</summary>

_Sourced from [Avalonia's
releases](https://github.com/AvaloniaUI/Avalonia//releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/AvaloniaUI/Avalonia//commits).
</details>

Updated [Avalonia.Desktop](https://github.com/AvaloniaUI/Avalonia/) from
11.3.20 to 11.3.22.

<details>
<summary>Release notes</summary>

_Sourced from [Avalonia.Desktop's
releases](https://github.com/AvaloniaUI/Avalonia//releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/AvaloniaUI/Avalonia//commits).
</details>

Updated [Avalonia.Diagnostics](https://github.com/AvaloniaUI/Avalonia/)
from 11.3.20 to 11.3.22.

<details>
<summary>Release notes</summary>

_Sourced from [Avalonia.Diagnostics's
releases](https://github.com/AvaloniaUI/Avalonia//releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/AvaloniaUI/Avalonia//commits).
</details>

Updated [Avalonia.Fonts.Inter](https://github.com/AvaloniaUI/Avalonia/)
from 11.3.20 to 11.3.22.

<details>
<summary>Release notes</summary>

_Sourced from [Avalonia.Fonts.Inter's
releases](https://github.com/AvaloniaUI/Avalonia//releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/AvaloniaUI/Avalonia//commits).
</details>

Updated
[Avalonia.Themes.Fluent](https://github.com/AvaloniaUI/Avalonia/) from
11.3.20 to 11.3.22.

<details>
<summary>Release notes</summary>

_Sourced from [Avalonia.Themes.Fluent's
releases](https://github.com/AvaloniaUI/Avalonia//releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/AvaloniaUI/Avalonia//commits).
</details>

Updated [Jint](https://github.com/sebastienros/jint) from 4.16.1 to
4.16.2.

<details>
<summary>Release notes</summary>

_Sourced from [Jint's
releases](https://github.com/sebastienros/jint/releases)._

## 4.16.2

Jint 4.16.2 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.1 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`](https://github.com/sebastienros/jint/blob/main/docs/v5-migration.md).

### Highlights

**Failures that used to end the process, or never end.** A native error
raised while a call's arguments are being evaluated is propagated
instead of leaving an empty value behind, which on 4.16.1 could recurse
until the process died — `decodeURIComponent` on a malformed sequence
was enough (#​4009). Native recursion and the forwarding paths through
bound functions and proxies are guarded so a deep native chain raises a
catchable error (#​4007). A module graph too deep to link raises an
error the host can catch instead of overflowing the stack (#​3548).
Temporal and Intl parsing cannot throw an uncatchable
`RegexMatchTimeoutException` because the machine was busy (#​3543), a
Temporal difference past a calendar's range raises `RangeError` instead
of spinning forever (#​3555), and the process-wide Intl culture cache
and Temporal zone cache are read-only and bounded, with a rejected zone
no longer remembered — closing a script-driven unbounded growth
(#​3546).

**Generators and built-ins, step by step.** A `yield*` delegation
reached again by a loop both re-delegates and keeps its place:
`countdown(3)` in a loop no longer hangs, and a delegating generator no
longer returns the memoized first result (#​3545). `Array.prototype.map`
and `slice` hand a `@@​species` constructor the length `ToLength`
produced, and a non-callable `map` argument is a `TypeError` (#​3547). A
trailing NUL pads neither a numeric string nor an array index (#​3552).
A removed property slot is a tombstone rather than a free slot to reuse,
so enumeration order survives a delete-and-readd (#​3318), and
`LengthOfArrayLike` no longer clamps through a `uint` overload (#​3328).

**Interop that answers for the right engine.** Two engines in one
process no longer decide each other's conversions and operators
(#​3559), a host type converter's answer stays with the engine whose
converter gave it (#​3563), and a value the host registers on a
`ShadowRealm` — and the members its wrapper builds eagerly — belong to
that realm (#​3557). Realm construction state is restored after nesting
or a failure (#​4008). Overload selection is by the arguments in hand:
an operator overload is chosen that way (#​3611), a `params` overload is
chosen by the array's element type with a failing element declining
rather than throwing (#​3782), an overload the argument cannot bind to
is not a match, and a host operator that throws reports what it threw
(#​3554). An index on a wrapped host collection is one property however
it is spelled, and a member filter that hides the indexer hides it
(#​3562); a read-only host collection refuses a write with a JavaScript
`TypeError` rather than the CLR's `NotSupportedException` (#​3556).

**Internationalization and Temporal.** The Persian calendar extends into
proleptic years on its 33-year cycle, so the ends of Temporal's range
land in the right Persian year (#​4006); a calendar that counts
Gregorian months writes their names (#​3612); and a `-u-` extension
carrying more than one key is read whole (#​3613).

**Errors.** Only a string-valued `stack` counts as a pre-existing stack
when a `JavaScriptException` is built, so an accessor or non-string
`stack` on a thrown object no longer breaks error reporting (#​3677,
reported by @​jeske).

Every change was verified failing-first against the unfixed branch on
both .NET Framework and .NET 10, and the release was gated on a paired
SunSpider and Dromaeo comparison against 4.16.1 on an idle machine: no
row regressed outside run-to-run noise, most run 1–4 % faster.

## What's Changed
* Backport: a removed property slot is a tombstone, not a free slot to
reuse (#​3273) by @​lahma in
sebastienros/jint#3318
* Backport: LengthOfArrayLike, delete the uint overload rather than
clamp it (#​3248) by @​lahma in
sebastienros/jint#3328
* Temporal and Intl parsing cannot fail because the machine was busy
(#​3486) by @​lahma in sebastienros/jint#3543
* Backport: the process-wide Intl culture cache and Temporal zone cache
are read-only and bounded, and a rejected zone is not remembered by
@​lahma in sebastienros/jint#3546
* Array: map and slice hand a @@​species constructor the length ToLength
produced (#​3510) by @​lahma in
sebastienros/jint#3547
* Generators: a yield* delegation both re-delegates and keeps its place
(backport of #​3506 and #​3518) by @​lahma in
sebastienros/jint#3545
* A module graph too deep to link raises an error the host can catch,
instead of ending the process (#​3415) by @​lahma in
sebastienros/jint#3548
* String to number: a trailing NUL pads neither a number string nor an
array index (backport of #​3544) by @​lahma in
sebastienros/jint#3552
* Interop: a host operator reports what it threw, and an overload the
argument cannot bind to is not a match by @​lahma in
sebastienros/jint#3554
* Temporal: a difference past a calendar's range raises RangeError
instead of spinning (#​3452) by @​lahma in
sebastienros/jint#3555
* Interop: a read-only host collection refuses script with a JavaScript
error, not the CLR's own (backport of #​3385) by @​lahma in
sebastienros/jint#3556
* ShadowRealm: a value the host registers, and the members its wrapper
builds eagerly, belong to that realm by @​lahma in
sebastienros/jint#3557
* Interop: two engines in one process do not decide each other's
conversions and operators (backport of #​3521 and #​3526) by @​lahma in
sebastienros/jint#3559
* Interop: an index on a wrapped host collection is one property, and a
filter that hides the indexer hides it by @​lahma in
sebastienros/jint#3562
* Interop: a host type converter's answer stays with the engine whose
converter gave it by @​lahma in
sebastienros/jint#3563
* Interop: an operator overload is chosen by the arguments in hand
(backport of #​3578) by @​lahma in
sebastienros/jint#3611
* Intl: a calendar counting Gregorian months writes their names
(backport of #​3589) by @​lahma in
sebastienros/jint#3612
* Intl: a `-u-` extension carrying more than one key is read whole
(backport of #​3594) by @​lahma in
sebastienros/jint#3613
* JavaScriptException: only a string "stack" counts as a pre-existing
stack (#​3607 backport) by @​lahma in
sebastienros/jint#3677
* Interop: a params overload is chosen by the array's element type, and
a failing element declines instead of throwing (#​3764) by @​lahma in
sebastienros/jint#3782
* Backport #​3751 to 4.x: Temporal: the persian calendar extends into
proleptic years on the 33-year cycle by @​lahma in
sebastienros/jint#4006
* Backport #​3922 to 4.x: Restore realm construction state after nesting
or failure by @​lahma in sebastienros/jint#4008
* Backport #​3845 to 4.x: Propagate native errors during call argument
evaluation by @​lahma in sebastienros/jint#4009
* Backport #​3877 to 4.x: Guard native recursion and forwarding paths by
@​lahma in sebastienros/jint#4007


**Full Changelog**:
sebastienros/jint@v4.16.1...v4.16.2


Commits viewable in [compare
view](sebastienros/jint@v4.16.1...v4.16.2).
</details>

Updated [Microsoft.AspNetCore](https://github.com/dotnet/aspnetcore)
from 2.3.12 to 2.3.13.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore's
releases](https://github.com/dotnet/aspnetcore/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/aspnetcore/commits).
</details>

Updated
[Microsoft.AspNetCore.Server.Kestrel.Core](https://github.com/dotnet/aspnetcore)
from 2.3.12 to 2.3.13.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Server.Kestrel.Core's
releases](https://github.com/dotnet/aspnetcore/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/aspnetcore/commits).
</details>

Updated
[System.Security.Cryptography.Xml](https://github.com/dotnet/dotnet)
from 10.0.11 to 10.0.12.

<details>
<summary>Release notes</summary>

_Sourced from [System.Security.Cryptography.Xml's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

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-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

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 <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
legrab added a commit to legrab/pocok that referenced this pull request Sep 15, 2026
Updated [Jint](https://github.com/sebastienros/jint) from 4.16.1 to
4.16.2.

<details>
<summary>Release notes</summary>

_Sourced from [Jint's
releases](https://github.com/sebastienros/jint/releases)._

## 4.16.2

Jint 4.16.2 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.1 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`](https://github.com/sebastienros/jint/blob/main/docs/v5-migration.md).

### Highlights

**Failures that used to end the process, or never end.** A native error
raised while a call's arguments are being evaluated is propagated
instead of leaving an empty value behind, which on 4.16.1 could recurse
until the process died — `decodeURIComponent` on a malformed sequence
was enough (#​4009). Native recursion and the forwarding paths through
bound functions and proxies are guarded so a deep native chain raises a
catchable error (#​4007). A module graph too deep to link raises an
error the host can catch instead of overflowing the stack (#​3548).
Temporal and Intl parsing cannot throw an uncatchable
`RegexMatchTimeoutException` because the machine was busy (#​3543), a
Temporal difference past a calendar's range raises `RangeError` instead
of spinning forever (#​3555), and the process-wide Intl culture cache
and Temporal zone cache are read-only and bounded, with a rejected zone
no longer remembered — closing a script-driven unbounded growth
(#​3546).

**Generators and built-ins, step by step.** A `yield*` delegation
reached again by a loop both re-delegates and keeps its place:
`countdown(3)` in a loop no longer hangs, and a delegating generator no
longer returns the memoized first result (#​3545). `Array.prototype.map`
and `slice` hand a `@@​species` constructor the length `ToLength`
produced, and a non-callable `map` argument is a `TypeError` (#​3547). A
trailing NUL pads neither a numeric string nor an array index (#​3552).
A removed property slot is a tombstone rather than a free slot to reuse,
so enumeration order survives a delete-and-readd (#​3318), and
`LengthOfArrayLike` no longer clamps through a `uint` overload (#​3328).

**Interop that answers for the right engine.** Two engines in one
process no longer decide each other's conversions and operators
(#​3559), a host type converter's answer stays with the engine whose
converter gave it (#​3563), and a value the host registers on a
`ShadowRealm` — and the members its wrapper builds eagerly — belong to
that realm (#​3557). Realm construction state is restored after nesting
or a failure (#​4008). Overload selection is by the arguments in hand:
an operator overload is chosen that way (#​3611), a `params` overload is
chosen by the array's element type with a failing element declining
rather than throwing (#​3782), an overload the argument cannot bind to
is not a match, and a host operator that throws reports what it threw
(#​3554). An index on a wrapped host collection is one property however
it is spelled, and a member filter that hides the indexer hides it
(#​3562); a read-only host collection refuses a write with a JavaScript
`TypeError` rather than the CLR's `NotSupportedException` (#​3556).

**Internationalization and Temporal.** The Persian calendar extends into
proleptic years on its 33-year cycle, so the ends of Temporal's range
land in the right Persian year (#​4006); a calendar that counts
Gregorian months writes their names (#​3612); and a `-u-` extension
carrying more than one key is read whole (#​3613).

**Errors.** Only a string-valued `stack` counts as a pre-existing stack
when a `JavaScriptException` is built, so an accessor or non-string
`stack` on a thrown object no longer breaks error reporting (#​3677,
reported by @​jeske).

Every change was verified failing-first against the unfixed branch on
both .NET Framework and .NET 10, and the release was gated on a paired
SunSpider and Dromaeo comparison against 4.16.1 on an idle machine: no
row regressed outside run-to-run noise, most run 1–4 % faster.

## What's Changed
* Backport: a removed property slot is a tombstone, not a free slot to
reuse (#​3273) by @​lahma in
sebastienros/jint#3318
* Backport: LengthOfArrayLike, delete the uint overload rather than
clamp it (#​3248) by @​lahma in
sebastienros/jint#3328
* Temporal and Intl parsing cannot fail because the machine was busy
(#​3486) by @​lahma in sebastienros/jint#3543
* Backport: the process-wide Intl culture cache and Temporal zone cache
are read-only and bounded, and a rejected zone is not remembered by
@​lahma in sebastienros/jint#3546
* Array: map and slice hand a @@​species constructor the length ToLength
produced (#​3510) by @​lahma in
sebastienros/jint#3547
* Generators: a yield* delegation both re-delegates and keeps its place
(backport of #​3506 and #​3518) by @​lahma in
sebastienros/jint#3545
* A module graph too deep to link raises an error the host can catch,
instead of ending the process (#​3415) by @​lahma in
sebastienros/jint#3548
* String to number: a trailing NUL pads neither a number string nor an
array index (backport of #​3544) by @​lahma in
sebastienros/jint#3552
* Interop: a host operator reports what it threw, and an overload the
argument cannot bind to is not a match by @​lahma in
sebastienros/jint#3554
* Temporal: a difference past a calendar's range raises RangeError
instead of spinning (#​3452) by @​lahma in
sebastienros/jint#3555
* Interop: a read-only host collection refuses script with a JavaScript
error, not the CLR's own (backport of #​3385) by @​lahma in
sebastienros/jint#3556
* ShadowRealm: a value the host registers, and the members its wrapper
builds eagerly, belong to that realm by @​lahma in
sebastienros/jint#3557
* Interop: two engines in one process do not decide each other's
conversions and operators (backport of #​3521 and #​3526) by @​lahma in
sebastienros/jint#3559
* Interop: an index on a wrapped host collection is one property, and a
filter that hides the indexer hides it by @​lahma in
sebastienros/jint#3562
* Interop: a host type converter's answer stays with the engine whose
converter gave it by @​lahma in
sebastienros/jint#3563
* Interop: an operator overload is chosen by the arguments in hand
(backport of #​3578) by @​lahma in
sebastienros/jint#3611
* Intl: a calendar counting Gregorian months writes their names
(backport of #​3589) by @​lahma in
sebastienros/jint#3612
* Intl: a `-u-` extension carrying more than one key is read whole
(backport of #​3594) by @​lahma in
sebastienros/jint#3613
* JavaScriptException: only a string "stack" counts as a pre-existing
stack (#​3607 backport) by @​lahma in
sebastienros/jint#3677
* Interop: a params overload is chosen by the array's element type, and
a failing element declines instead of throwing (#​3764) by @​lahma in
sebastienros/jint#3782
* Backport #​3751 to 4.x: Temporal: the persian calendar extends into
proleptic years on the 33-year cycle by @​lahma in
sebastienros/jint#4006
* Backport #​3922 to 4.x: Restore realm construction state after nesting
or failure by @​lahma in sebastienros/jint#4008
* Backport #​3845 to 4.x: Propagate native errors during call argument
evaluation by @​lahma in sebastienros/jint#4009
* Backport #​3877 to 4.x: Guard native recursion and forwarding paths by
@​lahma in sebastienros/jint#4007


**Full Changelog**:
sebastienros/jint@v4.16.1...v4.16.2


Commits viewable in [compare
view](sebastienros/jint@v4.16.1...v4.16.2).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Jint&package-manager=nuget&previous-version=4.16.1&new-version=4.16.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

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)


</details>
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