Skip to content

v4.13.0

Choose a tag to compare

@lahma lahma released this 15 Jul 12:39
c244f92

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