Skip to content

ShadowRealm: a value the host registers, and the members its wrapper builds eagerly, belong to that realm - #3557

Merged
lahma merged 1 commit into
sebastienros:4.xfrom
lahma:backport/3427-3367-shadowrealm-realms
Sep 1, 2026
Merged

lahma merged 1 commit into
sebastienros:4.xfrom
lahma:backport/3427-3367-shadowrealm-realms

Conversation

@lahma

@lahma lahma commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Backport of #3367 and #3427, the two ShadowRealm realm-affinity fixes, adapted to 4.x.

They land as one PR because the second only bites once the first exists. An ObjectWrapper takes its realm from whichever one is running when it is built, so until SetValue enters the shadow realm there is no shadow realm for the eagerly built members to belong to — porting #3427 alone would have been a no-op on 4.x.

A value the host registers belongs to that realm (#3325)

ShadowRealm.SetValue converted its argument against whichever realm the host called from — the principal one — and then installed the result on the shadow realm's global object. The prototype came from the wrong realm, so script inside the realm did not recognize the object the host had just handed it:

var shadowRealm = engine.Intrinsics.ShadowRealm.Construct();
shadowRealm.SetValue("company", new Company("acme"));

shadowRealm.Evaluate("company instanceof Object");   // false

That is the opposite of what a realm is for.

Realm-scoped construction is entering that realm's execution context, and nothing else. Engine.Realm is ExecutionContext.Realm, and every interop construction reads it to pick a prototype: JsValue.FromObject through ObjectInstance's base constructor, TypeReference.CreateTypeReference through TypeReferencePrototype, DelegateWrapper outright. ShadowRealmImportValue in this very class already does exactly that, so SetValue now brackets its registration the same way, through a RealmScope that names it.

The wrapper's eagerly built members agree with it (#3365)

ObjectWrapper's constructor builds three members eagerly — Symbol.dispose, Symbol.asyncDispose and toJSON — through the public ClrFunction(Engine, string, …) constructor, which pins engine._originalIntrinsics. That pin is deliberate and is what #2893 fixed: a function a host wires up against an engine must belong to the realm the surrounding script can reach whatever realm happened to be current when it was built. It is the wrong constructor for a function the engine builds for an object it is creating, and the consequence was two answers on one object:

handle.Dispose instanceof Function            // true   — resolved lazily, reads engine.Realm
handle[Symbol.dispose] instanceof Function    // false  — a Function of a realm this script cannot reach

using and JSON.stringify kept working throughout, because a call never consults the prototype. What did not work is anything asking what the member is: a feature detection, Object.getPrototypeOf, or a reach for call/apply/bind.

The issue asked which realm an ObjectWrapper answers with, and noted it has no _realm field. It needs none: the question is answered two constructors up, where ObjectInstance's constructor takes _prototype from engine.Realm.Intrinsics and ArrayLikeWrapper takes Array.prototype from engine.Intrinsics — both the running realm. The three members simply have to agree with it, so each now uses the internal ClrFunction(Engine, Realm, …) constructor whose own doc comment names this exact case, with engine.Realm.

ClrFunction(Engine, …), HostFunction and Constructor(Engine, string) keep pinning engine._originalIntrinsics and are untouched; HostClrFunctionRealmTests still passes. The distinction the two constructors draw is the whole rule: the host's function belongs to the host's realm; the engine's function belongs to the object's.

How this differs from main, and what is deliberately not here

Three overloads take the realm scope on 4.x rather than main's six — the same rule, not a narrower one. main's SetValue had been widened to mirror Engine.SetValue (#3321) before #3367 landed on it, so it has Type, generic and array overloads that 4.x does not. On 4.x a Type and an array both arrive through SetValue(string, object), and the four primitive overloads reach the JsValue one — which takes the scope as well, so installation happening in the realm being written stays one rule rather than two. Every path that converts or installs is covered, and the tests are written against the behaviour, not the overload list, so they pin the same seven facts.

Consequently main's GlobalValueRegistration hunk has no counterpart here — that class does not exist on 4.x, where the overloads convert inline.

The ImportValue half of #3367 is deliberately omitted: its premise is absent on 4.x. That half added using var ownership = _engine.EnterHostCall(); so a second thread reaching Evaluate during a module load is refused. 4.x has no EnterHostCall and no engine-ownership reservation at all — grep -rn "EnterHostCall" Jint/ returns nothing — so there is nothing to claim, no behaviour to fix, and no HostEngineConcurrencyTests to join. Porting the reservation would mean backporting the whole ownership mechanism, which is well past a one-sentence backport.

docs/v5-migration.md and Jint/Runtime/Interop/AGENTS.md do not exist on 4.x, and UndocumentedPublicApi.txt does not either, so those hunks are dropped. No public API signature moves — the constructor being called instead is internal and already existed on 4.x.

Evidence

Jint.Tests.PublicInterface/ShadowRealmValueRealmTests.cs, translated to xUnit v3 ([Test] → [Fact]): one fact per converting overload, one per eagerly built member, both negative halves stated against the principal intrinsic handed into the realm through the JsValue overload, the two guards that an engine registration keeps the principal realm, and the control that disposal itself still works. The Symbol.asyncDispose pair is #if !NETFRAMEWORK, matching Jint's own SUPPORTS_ASYNC_DISPOSE.

Failing first, on unmodified 4.x with only the tests added:

TFM before after
net10.0 Failed: 10, Passed: 3, Total: 13 Failed: 0, Passed: 13
net472 Failed: 9, Passed: 3, Total: 12 Failed: 0, Passed: 12

Per fix, on unfixed 4.x:

The 3 that pass on unfixed 4.x are exactly the two guards (TheEnginesOwnRegistrationsStillBelongToThePrincipalRealm, TheEagerlyBuiltMembersOfAnEngineRegistrationStayInThePrincipalRealm) and the control (ADisposableHostObjectIsStillDisposableInsideTheRealm) — which is the point: they state what must not change, and it did not.

Verification

  • dotnet build -c Release — 0 errors, 0 code warnings.
  • Jint.Tests — 6,958 passed on net10.0, 6,873 on net472, 0 failed.
  • Jint.Tests.PublicInterface — 1,515 passed on net10.0, 1,507 on net472, 0 failed.
  • Jint.Tests.CommonScripts — 28 passed on each, 0 failed.
  • Jint.Tests.SourceGenerators — 52 passed, 0 failed.
  • Jint.Tests.Test262 — 102,499 passed, 0 failed, 185 skipped of 102,684, matching the 4.x control exactly.

No benchmark: the change is which of two already-resolved prototype references three constructor-time allocations read, plus one execution-context push and pop per host SetValue call.

🤖 Generated with Claude Code

https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S

…builds eagerly, belong to that realm (backport of sebastienros#3367 and sebastienros#3325/sebastienros#3365)

Backport of the two ShadowRealm realm-affinity fixes, adapted to 4.x. They land together because
the second only bites once the first exists: an ObjectWrapper takes its realm from whichever one is
running when it is built, so until SetValue enters the shadow realm there is no shadow realm for
the eagerly built members to belong to.

SetValue converted its argument against whichever realm the host called from - the principal one -
and then installed the result on the shadow realm's global object, so the wrapper carried the
principal realm's Object.prototype and `company instanceof Object` inside the realm answered false.
That is the opposite of what a realm is for. Realm-scoped construction is entering that realm's
execution context and nothing else: Engine.Realm is ExecutionContext.Realm, and every interop
construction reads it to pick a prototype - JsValue.FromObject through ObjectInstance's base
constructor, TypeReference.CreateTypeReference through TypeReferencePrototype, DelegateWrapper
outright. ShadowRealmImportValue in this same class already does exactly that, so SetValue now
brackets its registration the same way through a RealmScope that names it.

Three overloads take the scope on 4.x rather than main's six, and that is the same rule rather than
a narrower one: 4.x has no SetValue(string, Type), no generic and no array overload, so a Type and
an array both arrive through SetValue(string, object), and the four primitive overloads reach the
JsValue one, which takes the scope as well. Installation happening in the realm being written is
one rule rather than two.

ObjectWrapper's constructor then builds three members eagerly - Symbol.dispose, Symbol.asyncDispose
and toJSON - through the public ClrFunction(Engine, string, ...) constructor, which pins
engine._originalIntrinsics. That pin is deliberate and is what sebastienros#2893 fixed: a function a *host*
wires up against an engine must belong to the realm the surrounding script can reach whatever realm
happened to be current when it was built. It is the wrong constructor for a function the engine
builds for an object it is creating, and the consequence was two answers on one object inside a
shadow realm - `handle.Dispose instanceof Function` true while
`handle[Symbol.dispose] instanceof Function` was false. Each now uses the internal
ClrFunction(Engine, Realm, ...) constructor whose own doc comment names this case, with
engine.Realm - the same realm ObjectInstance's constructor just took the object's own prototype
from. ClrFunction(Engine, ...), HostFunction and Constructor(Engine, string) are untouched, and
HostClrFunctionRealmTests still passes.

The other half of sebastienros#3367 - ImportValue taking a host-call reservation - is deliberately not here:
4.x has no EnterHostCall and no engine-ownership reservation at all, so there is nothing to claim
and no HostEngineConcurrencyTests to join.

Tests are Jint.Tests.PublicInterface/ShadowRealmValueRealmTests, translated to xUnit: one fact per
converting overload, one per eagerly built member, both negative halves stated against the
principal intrinsic handed into the realm through the JsValue overload, the two guards that an
engine registration keeps the principal realm, and the control that disposal itself still works.
Failing first on unfixed 4.x: 10 of 13 on net10.0, 9 of 12 on net472.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S
@lahma
lahma merged commit a943ec0 into sebastienros:4.x Sep 1, 2026
5 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