Skip to content

Interop: a read-only host collection refuses script with a JavaScript error, not the CLR's own (backport of #3385) - #3556

Merged
lahma merged 1 commit into
sebastienros:4.xfrom
lahma:backport/3385-readonly-collection-typeerror
Sep 1, 2026
Merged

lahma merged 1 commit into
sebastienros:4.xfrom
lahma:backport/3385-readonly-collection-typeerror

Conversation

@lahma

@lahma lahma commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Backport of #3385 (10b0955dc on main), which fixes #3382.

The observable change, stated plainly

This changes the exception type an embedder sees. Script that asks a read-only host collection to
change used to raise the CLR's System.NotSupportedException out of Engine.Evaluate; it now raises a
TypeError — in strict mode, or silently refuses in sloppy mode, depending on the operation. An embedder
whose code reads

try { engine.Evaluate(script); }
catch (NotSupportedException) { /* the script tried to write my read-only list */ }

will no longer see that exception, and one that has a bare catch (JavaScriptException) will start seeing
a refusal it never saw before. A catch (Exception) is unaffected.

It qualifies as a correctness fix rather than a feature all the same, for two reasons that are worth
separating. It is spec-required: push, pop, splice, sort and reverse are specified in terms
of Set(O, k, v, true) and DeletePropertyOrThrow, which raise a TypeError on a false [[Set]], and a
bare assignment is an ordinary [[Set]] returning false. And the exception it replaces was not
catchable from script at all
— neither a script try/catch nor a host catch (JavaScriptException)
could see it — so the behaviour being changed is one no script could handle and one whose only host-side
handler was a catch on a CLR type the engine never promised to raise. The maintainer should judge that
trade; it is the only behavioural change in the PR.

Before, on unmodified 4.x

Jint.Tests.PublicInterface/HostReadOnlyCollectionTests.cs was added to this branch on its own, with no
engine change, and run on both target frameworks. Six read-only shapes × ten operations × both modes, plus
the growable control, the message, and the ArraySegment<T> reclassification:

Target framework Failed Passed Total
net10.0 52 16 68
net472 52 16 68

Every one of the 52 is a System.NotSupportedException escaping Engine.Evaluate — there is not a single
other failure shape in the list. Two representative ones:

Failed …AnArrayGenericThatMustGrowAReadOnlyCollectionThrowsACatchableTypeError(shape: "ReadOnlyCollection", operation: "host.push(4)")
 System.NotSupportedException : Collection is read-only.
   at Jint.Runtime.Interop.ArrayLikeWrapper.SetAt(Int32 index, JsValue value)
   at Jint.Native.Array.ArrayPrototype.Push(JsValue thisObject, ReadOnlySpan`1 arguments)
   …
   at Jint.Engine.Evaluate(String code, String source)

Failed …TheRefusalIsTheOrdinaryReadOnlyPropertyTypeError(shape: "ImmutableList")
 System.NotSupportedException : Specified method is not supported.
   at Jint.Runtime.Throw.NotSupportedException(String message)
   at Jint.Runtime.Interop.ArrayLikeWrapper.SetAt(Int32 index, JsValue value)

The 16 that pass are the assignments ReadOnlyCollection<T>'s get-only indexer already refused by
accident, and the IReadOnlyList<T> length writes CanWrite => false already refused.

main measured 49/19 on the same file. The three extra failures here are ArrayList.ReadOnly's length
writes and delete, which on main are refused by #3381's ListWrapper.IsFixedSize — a PR that is not on
this branch and is not needed for this one.

After

Target framework Failed Passed Total
net10.0 0 68 68
net472 0 68 68

The TypeError an embedder actually reads is the ordinary one — Cannot assign to read only property '3'
of object '#<Object>'
— because the refusal now happens in [[Set]], before the collection is
reached at all. Nothing invents an interop-specific message.

What changed

One fact taken from the target, and three call sites:

  • ArrayLikeWrapper gains IsReadOnly; CanWrite becomes AllowWrite && !IsReadOnly and stops being
    virtual. ReadOnlyListWrapper<T>'s CanWrite => false override becomes IsReadOnly => true, which is
    the same statement about element writes plus the one it was missing about the length.
  • ListWrapper reads IList.IsReadOnly; GenericListWrapper<T> reads ICollection<T>.IsReadOnly.
  • The three lanes that consulted Options.Interop.AllowWrite directly rather than through CanWrite
    now consult CanWrite: ArrayLikeWrapper.Delete, ArrayLikeWrapper.SetAt, and ArrayOperations'
    ArrayLikeOperations.Set. Those three are precisely what made the mutators reachable for a read-only
    target.
  • Because of that the mutators are now unreachable for a read-only wrapper. Their bodies still refuse —
    ThrowReadOnly() instead of Throw.NotSupportedException() — as a documented backstop, so a lane added
    later cannot re-open the same hole.

The one carve-out

ICollection<T>.IsReadOnly cannot be taken at face value. System.Array and ArraySegment<T> both report
true through it to mean cannot grow, and both accept element writes — the non-generic IList asks
IsReadOnly and IsFixedSize separately and the generic interface collapsed them into one flag.
GenericListWrapper<T> therefore treats a T[] or an ArraySegment<T> target as fixed-size and
everything else's declaration as the truth. That reclassifies ArraySegment<T>, which is the point rather
than a side effect: it leaked NotSupportedException from push/pop/splice/a length write and
ArgumentOutOfRangeException from a write past the end, and now answers exactly as an int[] live view
does while keeping host[0] = 9.

Two adaptations this branch needed, and one thing left out

4.x's interop has diverged from main, so two hunks are not literal copies:

  1. ThrowFixedSize moves up from ArrayWrapper<T> (where it was private) to ArrayLikeWrapper (as
    protected), unchanged in message and behaviour, because the shared ThrowIfLengthCannotChange guard
    needs it. On main Native AOT: a degraded array view still refuses a resize, and the value-type generic sites are eight, not five #3381 had already hoisted it.
  2. The integral-index refusal in ArrayLikeWrapper.Set drops its numValue < Length bound, which is
    how the line already reads on main — it lost the bound in Disable projected CLR writes by default #3054 (Disable projected CLR writes by
    default
    ), a v5 change that is not on this branch. Without that removal a read-only target's growth
    write — which is exactly what push is — still fell through to the reflected indexer, so
    push/host[3] = 9 continued to leak NotSupportedException for the two shapes whose indexer setter
    throws rather than being get-only: a host IList<T> that declares IsReadOnly, and
    ArrayList.ReadOnly. Measured: with the rest of the port in place and this bound still present, 6 of
    the 68 cases still failed, all six with System.NotSupportedException : Collection is read-only.
    Removing it changes nothing else on this branch, because ObjectWrapper.Set already refuses when
    Interop.AllowWrite is off (!accessor.Writable || !AllowWrite) and when the wrapper is not extensible.

Deliberately not folded in: anything from the host-collection index cluster (#3356, #3416, #3464,
#3472, #3480, #3425). None of it is required here — the one line above predates #3416 and comes from #3054
instead — and it is being evaluated separately as a unit. #3381 is not needed either.

Also unchanged, exactly as on main: a growable List<int> still refuses list[3] = 9 with a raw
ArgumentOutOfRangeException while list.length = 5 grows it. Same defect class, different cause, out of
scope.

Verification

  • dotnet build -c Release — 0 errors. The one MSBuild warning (MSB3277, a package assembly-version
    conflict in Jint.Tests.CommonScripts' net472 leg) is pre-existing and unrelated.
  • Jint.Tests — 6,958 passed on net10.0, 6,873 on net472, 0 failed.
  • Jint.Tests.PublicInterface — 1,570 passed on net10.0, 1,563 on net472, 0 failed. The public API
    baselines did not move: CanWrite, IsReadOnly and the two refusals are all members of internal types.
  • Jint.Tests.CommonScripts — 28 passed on each of net10.0 and net472, 0 failed.
  • Jint.Tests.SourceGenerators — 52 passed, 0 failed.
  • Both suites again with JINT_HOST_CONTRACT_VERIFICATION=1 — 0 failed on both target frameworks.
  • Jint.Tests.Test262 — 102,499 passed, 0 failed, 185 skipped of 102,684, matching this branch's control
    exactly. The full run lost intl402/supportedLocalesOf-unicode-extensions-ignored.js in both modes to
    load on this shared machine (31 s each, against a 30 s engine default); the two pass in 2 s in isolation.
  • No benchmark: the read path (Get/GetJsValueAt) is untouched, and the write path trades one virtual
    CanWrite call for one virtual IsReadOnly call. Wrapping a host IList/IList<T> now reads one extra
    interface property once, at construction.

🤖 Generated with Claude Code

https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S

… error, not the CLR's own (backport of sebastienros#3385)

A wrapped collection that declares itself read-only raised NotSupportedException out of Evaluate when
script asked it to change - uncatchable by a script try/catch and by a host catch (JavaScriptException)
alike. Six shapes leaked: ReadOnlyCollection<T> and a host IList<T> whose IsReadOnly is true through
GenericListWrapper<T>, ImmutableList<T>, ImmutableArray<T> and anything reaching the engine as
IReadOnlyList<T> through ReadOnlyListWrapper<T> (whose mutators were literally Throw.NotSupportedException),
and ArrayList.ReadOnly through the untyped ListWrapper. pop and splice mutated the target part-way before
the CLR refused.

The refusal is now the engine's, and it is not the same refusal for every operation. push, pop, splice,
sort and reverse are specified as Set(O, k, v, true) and DeletePropertyOrThrow, so they raise a TypeError
in either mode; a bare assignment - host[0] = 9, host.length = 5, delete host[0] - is an ordinary [[Set]]
or [[Delete]] returning false, which is a TypeError in strict mode and silent in sloppy mode. Making
everything throw would have been wrong in exactly those five rows, and the collection is left untouched
in all of them.

ArrayLikeWrapper gains IsReadOnly, CanWrite becomes AllowWrite && !IsReadOnly and stops being virtual,
and the three lanes that consulted Options.Interop.AllowWrite directly - Delete, SetAt, and
ArrayOperations' array-like Set - consult CanWrite instead. Those three are exactly what made the mutators
reachable for a read-only target, so the mutators are now unreachable and their bodies are a documented
backstop rather than the fix.

ICollection<T>.IsReadOnly cannot be believed on its face. System.Array and ArraySegment<T> both report
true through it to mean "cannot grow" - the non-generic IList asks the two questions separately and the
generic one collapsed them - and both accept element writes. They are therefore treated as fixed-size,
which reclassifies ArraySegment<T>: it leaked NotSupportedException from push, pop, splice and a length
write, and ArgumentOutOfRangeException from a write past the end, and now answers exactly as an int[] live
view does while keeping arr[0] = 9.

Two adaptations this branch needs that main did not. ThrowFixedSize moves from ArrayWrapper<T> up to
ArrayLikeWrapper, unchanged, because the shared read-only/fixed-size guard needs it; and the integral-index
refusal in Set drops its numValue < Length bound, which on main came from sebastienros#3054's write-disable work.
Without that bound removed a read-only target's growth write - which is what push is - still fell through
to the reflected indexer and leaked the collection's own exception for the two shapes whose indexer setter
throws rather than being get-only, a host IList<T> and ArrayList.ReadOnly. It changes nothing else on this
branch: ObjectWrapper.Set already refuses when AllowWrite is off or the wrapper is not extensible.

Jint.Tests.PublicInterface/HostReadOnlyCollectionTests.cs pins the matrix from the embedder's side: 52 of
its 68 cases failed on unmodified 4.x - every one of them a System.NotSupportedException escaping
Engine.Evaluate - on both net10.0 and net472, and all 68 pass now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S
@lahma
lahma merged commit c6a8601 into sebastienros:4.x Sep 1, 2026
5 checks passed
lahma added a commit to lahma/jint that referenced this pull request Sep 1, 2026
… filter that hides the indexer hides it

Backport of eight main pull requests that together decide one question — what an index-shaped key means on a
wrapped CLR collection — plus the fix for the containment hole the seventh of them opened. They are one unit:
each of the first seven moves the answer, and taking any of them alone leaves the lanes disagreeing with each
other.

  sebastienros#3356  a host collection with a count is not a host collection with an index
  sebastienros#3381  a degraded array view still refuses a resize
  sebastienros#3425  the IndexWrappedOperations lane is not AOT-only, and a generic that must grow refuses
  sebastienros#3416  an index on a host collection is one property, however script spelled it
  sebastienros#3464  hasOwnProperty and "in" give one answer about an index on a wrapped collection
  sebastienros#3472  an index outside a wrapped collection is refused, not handed to the collection
  sebastienros#3480  a collection exposed as IList<T> or IReadOnlyList<T> gets the wrapper that contract names
  sebastienros#3561  a member filter that hides an indexer hides a wrapped collection's elements (fixes sebastienros#3558)

sebastienros#3385 - a read-only host collection refuses script with a JavaScript error - is the ninth member of the
cluster and is already on this branch as sebastienros#3556, so its hunks are not here. Its suite,
HostReadOnlyCollectionTests, is 68 of 68 green both before and after this change, which is what says so.

What script sees. An index-shaped key is now the view's own property, whichever way it is spelled and whether
or not the position exists. A read outside the range is undefined rather than the collection's own
ArgumentOutOfRangeException out of Evaluate; a write at the end grows a growable target exactly as a "length"
write of the same size does; "in", hasOwnProperty, propertyIsEnumerable and getOwnPropertyDescriptor give one
answer, because OrdinaryHasProperty is defined in terms of [[GetOwnProperty]] and may not disagree with it; a
delete of an absent position succeeds without reaching the collection; and a countable-but-not-indexable
target - Queue<T>, Stack<T>, LinkedList<T>, SortedSet<T> - is array-like with no element at index 0 rather
than an InvalidCastException from a lane that cast it to IList.

The containment half is why sebastienros#3561 is in the same change. Options.Interop.TypeResolver.MemberFilter is how a
host says which members script may reach, and an ArrayLikeWrapper answers every index-shaped key itself, so
the filter's decision about the indexer never reached the element lanes. On this branch that matters more than
it does on main: Interop.AllowWrite ships on here, so a filter that hid the indexer stopped nothing. Measured
on this branch, with the cluster applied and sebastienros#3561 held back, three refusals had become writes
(list[0] = 42, list['0'] = 42 and growth list[3] = 42) and reads, "in", delete, push and sort had never been
covered at all - and the pre-existing
HostIndexerFilterTests.AMemberFilterExcludingTheIndexerBlocksIndexedWrites, which passes on stock 4.x, fails.
The whole element contract is closed rather than only the write half, and containment is asked before the
read-only and fixed-size refusals of sebastienros#3382/sebastienros#3385 so the two compose: a fixed-size array whose indexer is
hidden reports "no such property" rather than the TypeError naming its bounds, which would answer a question
the host never granted.

Evidence, on net10.0 and net472 alike (identical counts on both). Against stock 4.x with the suites in place:
HostNonIndexedCollectionTests 33 of 45 failed, HostExposedCollectionTypeTests 21 of 33,
HostCollectionIndexWriteTests 56 of 63, HostCollectionIndexAgreementTests 11 of 18,
HostCollectionIndexBoundsTests 28 of 35, HostIndexerFilterTests 13 of 18, and 6 of the 7 new
InteropTests.ClrArrayLiveView cases. All of them pass now. The containment tests run in both write
configurations, because on this branch the default is the interesting one: the elements leak under
AllowWrite = true and the reads leak under AllowWrite = false, and both are pinned.

Deliberate divergences from main. sebastienros#3054 - which is what makes Interop.AllowWrite default to false there - is
a v5 default change and stays out, so this branch keeps its Delete and ArrayOperations.Set guards and the two
suites spell the write switch out where main could leave it to the default. Jint.AotExample's probes from
sebastienros#3381/sebastienros#3425/sebastienros#3480 are not ported: this branch's AotExample is a 22-line stub with none of the AOT probe
harness those hunks extend. docs/v5-migration.md and Jint/Runtime/Interop/AGENTS.md do not exist here, so
their hunks are carried into the XML docs and comments beside the code instead. The suites are xUnit v3 here
rather than the NUnit main moved to in sebastienros#3409.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S
lahma added a commit that referenced this pull request Sep 1, 2026
… filter that hides the indexer hides it (#3562)

Backport of eight main pull requests that together decide one question — what an index-shaped key means on a
wrapped CLR collection — plus the fix for the containment hole the seventh of them opened. They are one unit:
each of the first seven moves the answer, and taking any of them alone leaves the lanes disagreeing with each
other.

  #3356  a host collection with a count is not a host collection with an index
  #3381  a degraded array view still refuses a resize
  #3425  the IndexWrappedOperations lane is not AOT-only, and a generic that must grow refuses
  #3416  an index on a host collection is one property, however script spelled it
  #3464  hasOwnProperty and "in" give one answer about an index on a wrapped collection
  #3472  an index outside a wrapped collection is refused, not handed to the collection
  #3480  a collection exposed as IList<T> or IReadOnlyList<T> gets the wrapper that contract names
  #3561  a member filter that hides an indexer hides a wrapped collection's elements (fixes #3558)

#3385 - a read-only host collection refuses script with a JavaScript error - is the ninth member of the
cluster and is already on this branch as #3556, so its hunks are not here. Its suite,
HostReadOnlyCollectionTests, is 68 of 68 green both before and after this change, which is what says so.

What script sees. An index-shaped key is now the view's own property, whichever way it is spelled and whether
or not the position exists. A read outside the range is undefined rather than the collection's own
ArgumentOutOfRangeException out of Evaluate; a write at the end grows a growable target exactly as a "length"
write of the same size does; "in", hasOwnProperty, propertyIsEnumerable and getOwnPropertyDescriptor give one
answer, because OrdinaryHasProperty is defined in terms of [[GetOwnProperty]] and may not disagree with it; a
delete of an absent position succeeds without reaching the collection; and a countable-but-not-indexable
target - Queue<T>, Stack<T>, LinkedList<T>, SortedSet<T> - is array-like with no element at index 0 rather
than an InvalidCastException from a lane that cast it to IList.

The containment half is why #3561 is in the same change. Options.Interop.TypeResolver.MemberFilter is how a
host says which members script may reach, and an ArrayLikeWrapper answers every index-shaped key itself, so
the filter's decision about the indexer never reached the element lanes. On this branch that matters more than
it does on main: Interop.AllowWrite ships on here, so a filter that hid the indexer stopped nothing. Measured
on this branch, with the cluster applied and #3561 held back, three refusals had become writes
(list[0] = 42, list['0'] = 42 and growth list[3] = 42) and reads, "in", delete, push and sort had never been
covered at all - and the pre-existing
HostIndexerFilterTests.AMemberFilterExcludingTheIndexerBlocksIndexedWrites, which passes on stock 4.x, fails.
The whole element contract is closed rather than only the write half, and containment is asked before the
read-only and fixed-size refusals of #3382/#3385 so the two compose: a fixed-size array whose indexer is
hidden reports "no such property" rather than the TypeError naming its bounds, which would answer a question
the host never granted.

Evidence, on net10.0 and net472 alike (identical counts on both). Against stock 4.x with the suites in place:
HostNonIndexedCollectionTests 33 of 45 failed, HostExposedCollectionTypeTests 21 of 33,
HostCollectionIndexWriteTests 56 of 63, HostCollectionIndexAgreementTests 11 of 18,
HostCollectionIndexBoundsTests 28 of 35, HostIndexerFilterTests 13 of 18, and 6 of the 7 new
InteropTests.ClrArrayLiveView cases. All of them pass now. The containment tests run in both write
configurations, because on this branch the default is the interesting one: the elements leak under
AllowWrite = true and the reads leak under AllowWrite = false, and both are pinned.

Deliberate divergences from main. #3054 - which is what makes Interop.AllowWrite default to false there - is
a v5 default change and stays out, so this branch keeps its Delete and ArrayOperations.Set guards and the two
suites spell the write switch out where main could leave it to the default. Jint.AotExample's probes from
#3381/#3425/#3480 are not ported: this branch's AotExample is a 22-line stub with none of the AOT probe
harness those hunks extend. docs/v5-migration.md and Jint/Runtime/Interop/AGENTS.md do not exist here, so
their hunks are carried into the XML docs and comments beside the code instead. The suites are xUnit v3 here
rather than the NUnit main moved to in #3409.


Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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