v4.13.0
Jint 4.13.0 is a performance- and correctness-focused release. It brings a Proxy overhaul — trap dispatch rebuilt to forward with near-zero allocation, plus a new public API for implementing traps in .NET — extends the unboxed interpreter fast lanes to more operators and loop shapes, and cuts allocations on for..of, nested-function calls and array enumeration. A thorough pre-release review of everything since 4.12.0 also fixed several correctness bugs. No code changes are required to benefit.
Highlights
Proxy overhaul, and a CLR trap API. Proxy trap dispatch was rebuilt around a shared skeleton with lazy argument construction and pooled arrays, so a proxy with no matching trap forwards to its target with effectively zero allocation (#2674, #2675, #2676). Proxies can now be implemented from .NET: Engine.Advanced.CreateProxy / CreateRevocableProxy accept a ProxyHandler whose virtual methods are the traps, with the same invariant enforcement as JavaScript handlers (#2678). Several Proxy spec fixes came along — getPrototypeOf / setPrototypeOf with null prototypes (#2668), the construct trap's argument array (#2670), capturing [[Construct]] at creation (#2669), and the get trap firing for a property named revoke (#2667) — and the ObjectWrapper iterator helpers are hardened against foreign and revoked receivers (#2681).
Interpreter fast lanes. New unboxed operand lanes for the arithmetic binary operators (#2664) and an int32 fast lane for remainder (#2671) remove per-iteration boxing; flag-proven casts use Unsafe.As on the hot paths (#2673) and JsNumber.Create avoids a native fmod (#2662). Strict-equality guards against undefined / null / typeof are fused (#2658), member-expression identifier reads route through the identifier caches (#2660), and the identifier slot cache is restructured hop-0-first (#2689). The tight-loop fast lane now covers while and do-while bodies (#2688).
Lower allocations. for..of over an array no longer allocates an iterator-result object per element (#2700); per-call nested-function instantiation is allocation-free (#2684); for-in over arrays enumerates dense indices lazily without materializing a key list (#2656); and observation-only constraint checks are amortized so tight loops stay fast under a timeout (#2672).
RegExp. Quantified groups without capture or lookaround hazards prefer the .NET Regex engine (#2682), reused .NET adaptations adaptively upgrade to RegexOptions.Compiled (#2690), and the custom engine's match timeout is enforced by an inline deadline rather than a thread-pool timer (#2686).
Correctness (including a pre-release review). A review of everything since 4.12.0 fixed: a regex routing regression that silently truncated matches for nullable non-capturing quantified groups (#2694) and a custom-engine bug dropping iterations for multi-atom quantified groups (#2699); Proxy trap dispatch is now atomic against a mid-dispatch revoke (#2696); top-level await of a .NET Task in a module (#2665), plus prompt cancellation of the await drain (#2697); the arguments object escaping a short-circuiting logical compound assignment un-materialized (#2698); for-in now includes inherited enumerable index properties on Array.prototype (#2655); and the memory limit stays exact in tight loops (#2695).
Across the managed JavaScript engines for .NET, Jint 4.13.0 is the fastest engine on 17 of the 21 comparison scripts — and the fastest interpreter on all 21 — while allocating far less memory than the other engines; dromaeo-3d-cube is ~9% faster and dromaeo-string-base64 ~10% faster than 4.12.0. See the engine comparison benchmarks for the full table.
What's Changed
- Drop Jurassic from engine comparison benchmarks, add Okojo by @lahma in #2653
- Add benchmark lanes for for-in over arrays, holey-array traversal and large-chunk string concat by @lahma in #2654
- Fix for-in missing inherited enumerable index properties placed on Array.prototype by @lahma in #2655
- for-in over arrays: enumerate dense indices lazily without materializing a key list by @lahma in #2656
- Harden the known wall-clock test flakes by @lahma in #2657
- Fuse strict-equality guards: x === undefined/null and typeof x === "literal" by @lahma in #2658
- Skip the prototype walk for array hole reads when the chain is provably clean by @lahma in #2659
- Route member-expression identifier-object reads through identifier caches by @lahma in #2660
- Avoid native fmod in JsNumber.Create(double) integral detection by @lahma in #2662
- Add unboxed operand lane for arithmetic binary operators by @lahma in #2664
- Fix top-level await of a .NET Task in a module failing with "Pending" by @lahma in #2665
- Add Proxy trap dispatch benchmarks by @lahma in #2666
- Fix Proxy get trap being bypassed for properties named "revoke" by @lahma in #2667
- Add int32 fast lane to the raw-double remainder sites by @lahma in #2671
- Fix Proxy getPrototypeOf/setPrototypeOf traps with null prototypes by @lahma in #2668
- Capture Proxy [[Construct]] at creation per ProxyCreate by @lahma in #2669
- Replace flag-proven casts with Unsafe.As on hot arithmetic paths by @lahma in #2673
- Fix Proxy construct trap receiving Array-constructor-mangled arguments by @lahma in #2670
- Amortize observation-only constraint checks and keep tight loops armed under timeouts by @lahma in #2672
- Refactor Proxy trap dispatch into a shared skeleton with lazy argument construction by @lahma in #2674
- Pool Proxy trap argument arrays by @lahma in #2675
- Slim allocations in Proxy ownKeys trap validation by @lahma in #2676
- Give every Proxy invariant violation a descriptive TypeError message by @lahma in #2677
- Add ProxyHandler for implementing Proxy traps in .NET code by @lahma in #2678
- Guard default RevocableProxy instances with a clear exception by @lahma in #2679
- Add CLR ProxyHandler lanes to ProxyBenchmark by @lahma in #2680
- Harden ObjectWrapper iterator helpers against foreign and revoked receivers by @lahma in #2681
- Let branchy constructors with static this-stores shape from instance three by @lahma in #2685
- Prefer .NET Regex for quantified groups without capture or lookaround hazards by @lahma in #2682
- Skip covariant store checks when populating exact JsValue arrays by @lahma in #2683
- Enforce custom regex-engine timeout via an inline deadline instead of a thread-pool timer by @lahma in #2686
- Make per-call nested function instantiation allocation-free by @lahma in #2684
- CI: cross-OS Test262 suite cache + action/tool version bumps by @lahma in #2687
- Extend the tight-loop lane to while and do-while statements by @lahma in #2688
- Restructure identifier slot-cache hot paths as hop-0-first by @lahma in #2689
- Adaptively upgrade reused .NET regex adaptations to RegexOptions.Compiled by @lahma in #2690
- Update dependencies to latest version by @lahma in #2692
- Update Test262 suite to f2d143564 and drop now-fixed exclusion by @lahma in #2693
- Fix nullable non-capturing quantified regex groups mis-routed to .NET Regex by @lahma in #2694
- Keep the memory limit exact instead of amortizing it in tight loops by @lahma in #2695
- Make Proxy trap dispatch atomic against mid-dispatch revoke; guard ToObject() by @lahma in #2696
- Observe cancellation during the top-level-await event-loop drain by @lahma in #2697
- Materialize arguments escaping a short-circuiting logical compound assignment by @lahma in #2698
- Fix custom regex engine dropping iterations for a multi-atom quantified group by @lahma in #2699
- Skip per-element IteratorResult allocation in value-kind array for-of by @lahma in #2700
- Add value-producing slot fast path for identifier ++/-- expressions by @lahma in #2703
- Fuse x == null / x == undefined loose-equality guards by @lahma in #2702
- Compose &&/|| and ! through unboxed GetBooleanValue in boolean contexts by @lahma in #2701
Full Changelog: v4.12.0...v4.13.0