Skip to content

Emit OP_CLOSE_UPVALUE on break so closures pin per-iteration values#573

Merged
frostney merged 4 commits into
mainfrom
issue-537-break-close-upvalue
May 8, 2026
Merged

Emit OP_CLOSE_UPVALUE on break so closures pin per-iteration values#573
frostney merged 4 commits into
mainfrom
issue-537-break-close-upvalue

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented May 7, 2026

Summary

  • Fix bytecode compiler bug where break from loop bodies skipped per-iteration OP_CLOSE_UPVALUE, leaving closure cells attached to register slots
  • Add GBreakScopeDepth threadvar so CompileBreakStatement knows which scope depth to close down to, and emits OP_CLOSE_UPVALUE for any captured locals in intermediate scopes before the break jump
  • All 5 loop compilers save/set/restore the new threadvar: CompileCountedForOf, CompileForOfStatement, CompileForAwaitOfStatement, TryCompileCountedFor, CompileForStatement

Closes #537

Test plan

  • New tests/language/for-of/for-of-break-closure.js — 5 tests covering break+closure for general for-of, counted for-of, destructured binding, nested loops
  • New tests/language/for-loop/break-closure.js — 4 tests covering break+closure for traditional for-loops with let bindings
  • Opcode profiling confirms OP_CLOSE_UPVALUE now fires on the break path (was 0, now 1)
  • Full test suite passes in both bytecode (8850/8855) and interpreter (8848/8853) modes — only pre-existing FFI library failures
  • ./format.pas --check passes

🤖 Generated with Claude Code

…537)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
gocciascript-homepage Ignored Ignored Preview May 8, 2026 8:02am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9cd42daf-dc23-48ae-9316-88612886128d

📥 Commits

Reviewing files that changed from the base of the PR and between b53763e and a7d4d1e.

📒 Files selected for processing (1)
  • source/units/Goccia.Compiler.Statements.pas
✅ Files skipped from review due to trivial changes (1)
  • source/units/Goccia.Compiler.Statements.pas

📝 Walkthrough

Walkthrough

Introduces a thread-local GBreakScopeDepth variable to gate upvalue cleanup in break statements. All loop compilation paths now save, set, and restore this depth around their bodies. CompileBreakStatement uses this depth to determine which captured locals require OP_CLOSE_UPVALUE emission, fixing a bug where break could skip per-iteration cleanup and leave closures observing stale register values.

Changes

Break Upvalue Closure Fix

Layer / File(s) Summary
Scope Depth Tracking State
source/units/Goccia.Compiler.Statements.pas
Introduces GBreakScopeDepth threadvar to control break upvalue cleanup traversal.
Break Statement Cleanup Logic
source/units/Goccia.Compiler.Statements.pas
CompileBreakStatement now closes upvalues by scanning scope locals in reverse and stops when a local's depth is <= GBreakScopeDepth.
Loop Compiler Integration
source/units/Goccia.Compiler.Statements.pas
CompileCountedForOf, CompileForOfStatement, CompileForAwaitOfStatement, TryCompileCountedFor, and CompileForStatement save the previous GBreakScopeDepth, set it to their loop's scope depth during the per-iteration region, and restore it in finally/cleanup.
Tests / Coverage
tests/language/for-loop/break-closure.js, tests/language/for-of/for-of-break-closure.js
New tests validate closure capture semantics for break across for, for-of, nested loops, destructuring, register reuse, try/finally, and for await...of scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #539: Shares focus on loop per-iteration scope handling and OP_CLOSE_UPVALUE emission for break paths; this PR appears to implement the fix described there.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: emitting OP_CLOSE_UPVALUE on break to fix closure behavior with per-iteration values in loops.
Description check ✅ Passed The PR description comprehensively covers the bug fix, implementation details, affected compiler sites, test coverage, and verification steps, following the template structure.
Linked Issues check ✅ Passed All coding requirements from issue #537 are met: GBreakScopeDepth threadvar implementation [#537], OP_CLOSE_UPVALUE emission on break paths [#537], all five loop compilers modified [#537], and comprehensive test coverage [#537].
Out of Scope Changes check ✅ Passed All changes are directly related to the issue #537 requirements: compiler modification to emit OP_CLOSE_UPVALUE on break, and corresponding test files validating the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added bug Something isn't working spec compliance Mismatch against official JavaScript/TypeScript specification internal Refactoring, CI, tooling, cleanup labels May 7, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Compiler.Statements.pas`:
- Around line 2969-2976: The close-upvalue loop uses GBreakScopeDepth to decide
which locals to keep, but CompileSwitchStatement doesn't initialize
GBreakScopeDepth so breaks from switch may use a stale value; fix by having
CompileSwitchStatement save the old GBreakScopeDepth, set GBreakScopeDepth to
the switch's intended break target depth (e.g. use ACtx.Scope.Depth or the scope
depth representing the switch's exit) before compiling case bodies / emitting
OP_BREAK, then restore the saved GBreakScopeDepth afterward so the for-loop in
the close-upvalue pass (the code iterating ACtx.Scope locals and checking
Local.Depth <= GBreakScopeDepth) sees the correct depth and won't close outer
captured locals mistakenly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8c66c9e-8557-4f8a-8639-494453c10eb1

📥 Commits

Reviewing files that changed from the base of the PR and between 26834bd and 8939510.

📒 Files selected for processing (3)
  • source/units/Goccia.Compiler.Statements.pas
  • tests/language/for-loop/break-closure.js
  • tests/language/for-of/for-of-break-closure.js

Comment thread source/units/Goccia.Compiler.Statements.pas
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Benchmark Results

407 benchmarks

Interpreted: 🟢 393 improved · 🔴 11 regressed · 3 unchanged · avg +38.7%
Bytecode: 🟢 242 improved · 🔴 27 regressed · 138 unchanged · avg +4.0%

arraybuffer.js — Interp: 🟢 14 · avg +40.8% · Bytecode: 🟢 9, 🔴 1, 4 unch. · avg +3.4%
Benchmark Interpreted Δ Bytecode Δ
create ArrayBuffer(0) 147,043 ops/sec [111,644..162,316] → 228,348 ops/sec [210,600..230,729] 🟢 +55.3% 210,046 ops/sec [159,236..215,749] → 218,541 ops/sec [213,857..223,136] ~ overlap (+4.0%)
create ArrayBuffer(64) 154,461 ops/sec [145,194..157,160] → 217,300 ops/sec [214,075..218,157] 🟢 +40.7% 201,824 ops/sec [200,742..204,966] → 210,825 ops/sec [210,593..211,162] 🟢 +4.5%
create ArrayBuffer(1024) 136,676 ops/sec [134,957..138,242] → 184,838 ops/sec [182,549..187,635] 🟢 +35.2% 175,711 ops/sec [173,774..177,425] → 173,191 ops/sec [167,858..176,668] ~ overlap (-1.4%)
create ArrayBuffer(8192) 76,875 ops/sec [76,244..77,596] → 97,368 ops/sec [96,171..97,843] 🟢 +26.7% 98,490 ops/sec [98,047..99,182] → 84,692 ops/sec [83,887..84,971] 🔴 -14.0%
slice full buffer (64 bytes) 184,603 ops/sec [180,466..186,368] → 256,653 ops/sec [253,962..258,194] 🟢 +39.0% 253,856 ops/sec [249,066..256,767] → 270,985 ops/sec [257,911..272,642] 🟢 +6.7%
slice half buffer (512 of 1024 bytes) 163,119 ops/sec [159,237..167,817] → 228,942 ops/sec [226,519..230,227] 🟢 +40.4% 222,098 ops/sec [219,358..223,981] → 232,852 ops/sec [229,307..235,435] 🟢 +4.8%
slice with negative indices 128,901 ops/sec [76,627..151,257] → 211,179 ops/sec [187,899..214,831] 🟢 +63.8% 233,295 ops/sec [228,101..241,036] → 241,386 ops/sec [216,985..246,905] ~ overlap (+3.5%)
slice empty range 173,968 ops/sec [172,105..176,662] → 241,020 ops/sec [240,059..244,433] 🟢 +38.5% 242,006 ops/sec [240,262..247,332] → 253,882 ops/sec [251,198..258,017] 🟢 +4.9%
byteLength access 397,295 ops/sec [385,993..400,711] → 559,021 ops/sec [553,087..564,703] 🟢 +40.7% 476,322 ops/sec [473,603..478,324] → 523,746 ops/sec [516,529..533,452] 🟢 +10.0%
Symbol.toStringTag access 329,785 ops/sec [329,022..330,415] → 451,175 ops/sec [445,428..461,113] 🟢 +36.8% 342,659 ops/sec [339,234..347,746] → 375,989 ops/sec [370,539..380,692] 🟢 +9.7%
ArrayBuffer.isView 239,087 ops/sec [235,877..240,545] → 338,501 ops/sec [333,628..344,283] 🟢 +41.6% 316,921 ops/sec [315,763..317,610] → 342,069 ops/sec [336,248..344,754] 🟢 +7.9%
clone ArrayBuffer(64) 163,642 ops/sec [163,395..164,439] → 227,140 ops/sec [224,293..233,701] 🟢 +38.8% 223,354 ops/sec [220,694..223,939] → 235,084 ops/sec [233,839..236,430] 🟢 +5.3%
clone ArrayBuffer(1024) 140,844 ops/sec [138,493..143,399] → 192,225 ops/sec [187,934..194,469] 🟢 +36.5% 191,749 ops/sec [188,814..194,049] → 189,477 ops/sec [187,947..191,237] ~ overlap (-1.2%)
clone ArrayBuffer inside object 116,435 ops/sec [113,162..117,239] → 159,457 ops/sec [157,979..160,238] 🟢 +36.9% 148,698 ops/sec [146,576..149,847] → 153,197 ops/sec [150,251..154,003] 🟢 +3.0%
arrays.js — Interp: 🟢 19 · avg +40.3% · Bytecode: 🟢 13, 6 unch. · avg +4.9%
Benchmark Interpreted Δ Bytecode Δ
Array.from length 100 3,798 ops/sec [3,779..3,824] → 5,452 ops/sec [5,340..5,636] 🟢 +43.6% 6,308 ops/sec [6,168..6,310] → 7,070 ops/sec [6,808..7,159] 🟢 +12.1%
Array.from 10 elements 86,881 ops/sec [86,664..87,408] → 123,201 ops/sec [122,680..126,212] 🟢 +41.8% 97,823 ops/sec [97,171..97,947] → 104,626 ops/sec [103,979..104,777] 🟢 +7.0%
Array.of 10 elements 105,100 ops/sec [103,543..107,309] → 151,232 ops/sec [149,611..154,910] 🟢 +43.9% 124,450 ops/sec [123,840..124,743] → 134,028 ops/sec [132,931..134,339] 🟢 +7.7%
spread into new array 130,216 ops/sec [127,186..133,009] → 183,737 ops/sec [181,691..184,211] 🟢 +41.1% 80,465 ops/sec [79,954..81,319] → 82,040 ops/sec [81,610..82,999] 🟢 +2.0%
map over 50 elements 6,420 ops/sec [6,355..6,569] → 9,287 ops/sec [9,175..9,459] 🟢 +44.7% 12,377 ops/sec [12,284..12,427] → 12,819 ops/sec [12,730..12,899] 🟢 +3.6%
filter over 50 elements 6,226 ops/sec [6,039..6,286] → 8,824 ops/sec [8,626..8,959] 🟢 +41.7% 12,163 ops/sec [12,066..12,356] → 12,531 ops/sec [12,353..12,677] ~ overlap (+3.0%)
reduce sum 50 elements 6,747 ops/sec [6,594..6,784] → 9,543 ops/sec [9,264..9,911] 🟢 +41.4% 11,541 ops/sec [11,405..11,607] → 11,892 ops/sec [11,047..12,207] ~ overlap (+3.0%)
forEach over 50 elements 6,002 ops/sec [5,852..6,055] → 8,434 ops/sec [8,353..8,612] 🟢 +40.5% 12,599 ops/sec [12,418..12,635] → 12,817 ops/sec [12,665..13,333] 🟢 +1.7%
find in 50 elements 8,939 ops/sec [8,360..9,061] → 12,609 ops/sec [12,401..12,712] 🟢 +41.1% 17,578 ops/sec [17,224..17,727] → 18,654 ops/sec [18,360..18,774] 🟢 +6.1%
sort 20 elements 3,341 ops/sec [3,261..3,480] → 4,737 ops/sec [4,612..4,792] 🟢 +41.8% 6,680 ops/sec [6,600..6,710] → 7,268 ops/sec [7,232..7,294] 🟢 +8.8%
flat nested array 45,680 ops/sec [44,526..46,243] → 64,460 ops/sec [63,921..65,461] 🟢 +41.1% 53,423 ops/sec [52,336..54,481] → 54,668 ops/sec [53,405..55,296] ~ overlap (+2.3%)
flatMap 26,431 ops/sec [25,956..26,689] → 36,140 ops/sec [35,744..36,734] 🟢 +36.7% 35,660 ops/sec [35,339..36,199] → 35,922 ops/sec [35,095..36,320] ~ overlap (+0.7%)
map inside map (5x5) 6,893 ops/sec [6,772..6,979] → 9,456 ops/sec [9,431..9,525] 🟢 +37.2% 10,325 ops/sec [10,255..10,449] → 10,572 ops/sec [10,409..10,612] ~ overlap (+2.4%)
filter inside map (5x10) 4,900 ops/sec [4,680..4,943] → 6,832 ops/sec [6,718..6,865] 🟢 +39.5% 8,298 ops/sec [8,178..8,362] → 8,582 ops/sec [8,363..8,804] 🟢 +3.4%
reduce inside map (5x10) 5,361 ops/sec [5,232..5,392] → 7,735 ops/sec [7,583..7,762] 🟢 +44.3% 8,914 ops/sec [8,861..9,020] → 9,407 ops/sec [9,186..9,471] 🟢 +5.5%
forEach inside forEach (5x10) 4,801 ops/sec [4,717..4,842] → 6,782 ops/sec [6,732..6,828] 🟢 +41.3% 9,695 ops/sec [9,664..9,792] → 10,288 ops/sec [10,212..10,329] 🟢 +6.1%
find inside some (10x10) 3,944 ops/sec [3,894..4,086] → 5,500 ops/sec [5,469..5,710] 🟢 +39.5% 7,310 ops/sec [7,267..7,371] → 7,967 ops/sec [7,895..7,998] 🟢 +9.0%
map+filter chain nested (5x20) 1,514 ops/sec [1,509..1,541] → 2,031 ops/sec [2,014..2,085] 🟢 +34.1% 2,655 ops/sec [2,643..2,670] → 2,883 ops/sec [2,864..2,916] 🟢 +8.6%
reduce flatten (10x5) 16,293 ops/sec [16,048..16,454] → 21,267 ops/sec [21,124..21,614] 🟢 +30.5% 7,658 ops/sec [7,558..7,703] → 7,634 ops/sec [6,781..7,795] ~ overlap (-0.3%)
async-await.js — Interp: 🟢 5, 1 unch. · avg +42.8% · Bytecode: 🟢 2, 4 unch. · avg +3.0%
Benchmark Interpreted Δ Bytecode Δ
single await 136,636 ops/sec [113,201..140,082] → 198,374 ops/sec [129,798..201,485] ~ overlap (+45.2%) 162,167 ops/sec [157,783..164,597] → 165,935 ops/sec [140,079..169,424] ~ overlap (+2.3%)
multiple awaits 66,491 ops/sec [64,860..67,778] → 94,381 ops/sec [93,832..95,928] 🟢 +41.9% 69,908 ops/sec [69,631..69,964] → 73,101 ops/sec [72,682..75,026] 🟢 +4.6%
await non-Promise value 264,615 ops/sec [261,195..264,707] → 380,210 ops/sec [377,798..385,967] 🟢 +43.7% 436,841 ops/sec [433,352..442,870] → 439,681 ops/sec [433,828..443,919] ~ overlap (+0.7%)
await with try/catch 113,904 ops/sec [111,716..114,238] → 161,748 ops/sec [159,452..162,488] 🟢 +42.0% 157,605 ops/sec [157,123..158,909] → 159,065 ops/sec [158,399..159,398] ~ overlap (+0.9%)
await Promise.all 22,186 ops/sec [21,852..23,270] → 31,471 ops/sec [30,704..32,417] 🟢 +41.9% 23,585 ops/sec [23,417..23,804] → 24,450 ops/sec [23,882..24,675] 🟢 +3.7%
nested async function call 72,819 ops/sec [72,259..73,243] → 103,656 ops/sec [103,210..104,810] 🟢 +42.3% 97,094 ops/sec [66,077..99,601] → 102,690 ops/sec [94,994..104,066] ~ overlap (+5.8%)
async-generators.js — Interp: 🟢 2 · avg +47.5% · Bytecode: 🟢 1, 1 unch. · avg +1.3%
Benchmark Interpreted Δ Bytecode Δ
for-await-of over async generator 2,227 ops/sec [1,927..2,338] → 3,327 ops/sec [3,268..3,358] 🟢 +49.4% 2,860 ops/sec [2,348..2,879] → 2,862 ops/sec [2,467..2,891] ~ overlap (+0.1%)
async generator with await in body 20,823 ops/sec [20,725..21,009] → 30,321 ops/sec [30,010..30,688] 🟢 +45.6% 24,351 ops/sec [23,901..24,665] → 24,987 ops/sec [24,709..25,177] 🟢 +2.6%
base64.js — Interp: 🟢 10 · avg +42.5% · Bytecode: 🟢 8, 2 unch. · avg +4.6%
Benchmark Interpreted Δ Bytecode Δ
short ASCII (13 chars) 3,656 ops/sec [3,607..3,720] → 5,262 ops/sec [5,159..5,385] 🟢 +43.9% 4,088 ops/sec [4,048..4,147] → 4,072 ops/sec [3,994..4,105] ~ overlap (-0.4%)
medium ASCII (450 chars) 136 ops/sec [129..144] → 195 ops/sec [184..202] 🟢 +43.3% 150 ops/sec [135..152] → 155 ops/sec [153..159] 🟢 +3.4%
Latin-1 characters 5,269 ops/sec [5,242..5,281] → 7,563 ops/sec [7,534..7,703] 🟢 +43.6% 5,832 ops/sec [5,715..5,954] → 6,095 ops/sec [6,045..6,157] 🟢 +4.5%
short base64 (20 chars) 2,285 ops/sec [1,446..2,334] → 3,305 ops/sec [3,257..3,328] 🟢 +44.6% 2,455 ops/sec [2,413..2,476] → 2,606 ops/sec [2,435..2,644] ~ overlap (+6.1%)
medium base64 (600 chars) 97 ops/sec [96..99] → 137 ops/sec [135..141] 🟢 +41.7% 101 ops/sec [100..102] → 107 ops/sec [106..109] 🟢 +6.0%
Latin-1 output 3,290 ops/sec [3,254..3,330] → 4,709 ops/sec [4,691..4,715] 🟢 +43.1% 3,519 ops/sec [3,487..3,583] → 3,760 ops/sec [3,744..3,782] 🟢 +6.9%
forgiving (no padding) 4,877 ops/sec [4,856..4,904] → 7,114 ops/sec [7,010..7,194] 🟢 +45.9% 5,188 ops/sec [5,161..5,221] → 5,645 ops/sec [5,597..5,724] 🟢 +8.8%
with whitespace 2,123 ops/sec [2,081..2,144] → 2,979 ops/sec [2,925..3,027] 🟢 +40.3% 2,240 ops/sec [2,216..2,257] → 2,330 ops/sec [2,313..2,353] 🟢 +4.0%
atob(btoa(short)) 1,442 ops/sec [1,436..1,450] → 1,991 ops/sec [1,983..2,040] 🟢 +38.0% 1,549 ops/sec [1,534..1,554] → 1,594 ops/sec [1,584..1,602] 🟢 +2.9%
atob(btoa(medium)) 57 ops/sec [56..58] → 80 ops/sec [79..80] 🟢 +40.5% 60 ops/sec [60..61] → 63 ops/sec [63..63] 🟢 +4.1%
classes.js — Interp: 🟢 31 · avg +36.0% · Bytecode: 🟢 12, 🔴 1, 18 unch. · avg +2.6%
Benchmark Interpreted Δ Bytecode Δ
simple class new 49,353 ops/sec [37,970..52,543] → 73,533 ops/sec [73,010..75,416] 🟢 +49.0% 71,808 ops/sec [71,087..72,810] → 75,852 ops/sec [75,226..76,390] 🟢 +5.6%
class with defaults 43,452 ops/sec [42,943..43,586] → 60,926 ops/sec [60,372..61,121] 🟢 +40.2% 50,470 ops/sec [49,456..50,749] → 54,634 ops/sec [53,619..55,050] 🟢 +8.3%
50 instances via Array.from 1,954 ops/sec [1,945..1,963] → 2,746 ops/sec [2,743..2,749] 🟢 +40.5% 2,709 ops/sec [2,697..2,720] → 2,889 ops/sec [2,836..2,922] 🟢 +6.7%
instance method call 25,559 ops/sec [25,386..25,681] → 36,263 ops/sec [36,146..36,642] 🟢 +41.9% 34,535 ops/sec [34,213..34,677] → 37,433 ops/sec [36,962..37,565] 🟢 +8.4%
static method call 41,088 ops/sec [39,949..41,418] → 57,267 ops/sec [57,092..57,574] 🟢 +39.4% 72,505 ops/sec [72,235..72,813] → 76,265 ops/sec [75,724..77,423] 🟢 +5.2%
single-level inheritance 20,861 ops/sec [20,510..21,292] → 30,250 ops/sec [29,984..30,432] 🟢 +45.0% 27,353 ops/sec [27,135..27,698] → 28,576 ops/sec [28,286..29,112] 🟢 +4.5%
two-level inheritance 18,384 ops/sec [18,196..18,574] → 25,872 ops/sec [25,745..25,892] 🟢 +40.7% 22,058 ops/sec [21,632..22,286] → 22,670 ops/sec [22,408..22,836] 🟢 +2.8%
private field access 28,434 ops/sec [28,329..28,502] → 39,722 ops/sec [39,487..40,062] 🟢 +39.7% 24,631 ops/sec [24,451..24,857] → 26,544 ops/sec [26,346..26,751] 🟢 +7.8%
private methods 31,364 ops/sec [31,001..31,594] → 43,596 ops/sec [43,040..43,851] 🟢 +39.0% 28,434 ops/sec [27,755..28,611] → 30,723 ops/sec [30,257..31,024] 🟢 +8.1%
getter/setter access 28,700 ops/sec [28,507..28,834] → 39,710 ops/sec [39,073..40,021] 🟢 +38.4% 40,011 ops/sec [39,539..40,329] → 41,721 ops/sec [41,373..41,897] 🟢 +4.3%
class decorator (identity) 39,175 ops/sec [38,393..39,672] → 53,358 ops/sec [52,443..54,150] 🟢 +36.2% 45,266 ops/sec [43,551..45,882] → 46,959 ops/sec [46,180..47,764] 🟢 +3.7%
class decorator (wrapping) 22,578 ops/sec [22,275..23,391] → 31,488 ops/sec [30,674..32,083] 🟢 +39.5% 24,629 ops/sec [24,451..25,012] → 24,058 ops/sec [23,161..24,416] 🔴 -2.3%
identity method decorator 29,057 ops/sec [28,808..29,368] → 39,276 ops/sec [39,164..39,689] 🟢 +35.2% 38,683 ops/sec [37,905..40,660] → 40,038 ops/sec [38,983..41,295] ~ overlap (+3.5%)
wrapping method decorator 23,584 ops/sec [23,464..23,716] → 32,282 ops/sec [31,496..32,976] 🟢 +36.9% 29,721 ops/sec [28,724..30,613] → 28,827 ops/sec [28,389..29,836] ~ overlap (-3.0%)
stacked method decorators (x3) 15,982 ops/sec [15,592..16,773] → 22,352 ops/sec [21,989..23,085] 🟢 +39.9% 20,529 ops/sec [19,980..21,194] → 22,289 ops/sec [20,504..23,634] ~ overlap (+8.6%)
identity field decorator 32,123 ops/sec [31,877..32,790] → 41,925 ops/sec [40,762..43,253] 🟢 +30.5% 35,763 ops/sec [34,983..36,595] → 35,370 ops/sec [33,602..36,292] ~ overlap (-1.1%)
field initializer decorator 26,928 ops/sec [26,418..27,501] → 35,586 ops/sec [32,651..36,301] 🟢 +32.2% 31,006 ops/sec [29,549..32,482] → 31,303 ops/sec [31,233..31,777] ~ overlap (+1.0%)
getter decorator (identity) 29,163 ops/sec [29,131..29,184] → 39,401 ops/sec [38,123..40,044] 🟢 +35.1% 29,296 ops/sec [29,186..29,451] → 29,610 ops/sec [28,600..30,116] ~ overlap (+1.1%)
setter decorator (identity) 23,875 ops/sec [23,588..24,109] → 32,416 ops/sec [32,317..32,832] 🟢 +35.8% 23,970 ops/sec [23,851..24,132] → 23,916 ops/sec [22,925..24,733] ~ overlap (-0.2%)
static method decorator 30,812 ops/sec [29,952..31,438] → 40,829 ops/sec [40,313..41,598] 🟢 +32.5% 37,547 ops/sec [37,111..39,367] → 38,293 ops/sec [36,659..39,300] ~ overlap (+2.0%)
static field decorator 36,621 ops/sec [36,192..36,662] → 48,946 ops/sec [47,766..50,807] 🟢 +33.7% 42,954 ops/sec [40,340..44,392] → 42,722 ops/sec [41,117..43,912] ~ overlap (-0.5%)
private method decorator 24,029 ops/sec [23,780..25,045] → 32,228 ops/sec [31,923..32,980] 🟢 +34.1% 31,131 ops/sec [30,592..31,648] → 29,756 ops/sec [28,749..31,088] ~ overlap (-4.4%)
private field decorator 26,815 ops/sec [26,380..27,495] → 35,977 ops/sec [35,269..37,458] 🟢 +34.2% 25,774 ops/sec [25,120..26,242] → 26,525 ops/sec [26,115..26,835] ~ overlap (+2.9%)
plain auto-accessor (no decorator) 45,567 ops/sec [44,544..46,541] → 57,903 ops/sec [57,033..62,364] 🟢 +27.1% 43,489 ops/sec [41,939..46,228] → 42,616 ops/sec [41,876..43,272] ~ overlap (-2.0%)
auto-accessor with decorator 25,877 ops/sec [25,593..26,973] → 34,132 ops/sec [33,113..37,386] 🟢 +31.9% 26,748 ops/sec [25,482..27,893] → 27,722 ops/sec [26,536..28,508] ~ overlap (+3.6%)
decorator writing metadata 20,685 ops/sec [20,464..21,038] → 27,579 ops/sec [27,113..28,182] 🟢 +33.3% 24,481 ops/sec [23,963..25,210] → 24,494 ops/sec [24,291..25,547] ~ overlap (+0.1%)
static getter read 53,614 ops/sec [53,053..54,121] → 68,851 ops/sec [68,349..70,131] 🟢 +28.4% 70,094 ops/sec [67,793..71,542] → 71,030 ops/sec [70,121..71,365] ~ overlap (+1.3%)
static getter/setter pair 39,145 ops/sec [38,750..39,395] → 51,228 ops/sec [50,553..52,250] 🟢 +30.9% 50,341 ops/sec [49,692..51,997] → 53,213 ops/sec [52,843..53,399] 🟢 +5.7%
inherited static getter 31,696 ops/sec [31,313..32,154] → 41,530 ops/sec [40,855..41,942] 🟢 +31.0% 39,045 ops/sec [38,748..39,162] → 38,812 ops/sec [37,788..39,167] ~ overlap (-0.6%)
inherited static setter 34,414 ops/sec [33,800..34,543] → 44,162 ops/sec [43,981..44,989] 🟢 +28.3% 41,503 ops/sec [41,238..41,721] → 41,943 ops/sec [41,685..42,434] ~ overlap (+1.1%)
inherited static getter with this binding 28,031 ops/sec [27,644..28,135] → 37,680 ops/sec [37,038..37,775] 🟢 +34.4% 34,853 ops/sec [34,515..35,195] → 34,579 ops/sec [33,873..34,884] ~ overlap (-0.8%)
closures.js — Interp: 🟢 11 · avg +40.7% · Bytecode: 🟢 9, 2 unch. · avg +7.8%
Benchmark Interpreted Δ Bytecode Δ
closure over single variable 43,510 ops/sec [42,435..44,269] → 60,332 ops/sec [59,366..60,958] 🟢 +38.7% 153,867 ops/sec [151,761..154,495] → 164,077 ops/sec [163,027..165,937] 🟢 +6.6%
closure over multiple variables 44,241 ops/sec [43,500..44,789] → 63,162 ops/sec [61,851..64,511] 🟢 +42.8% 134,844 ops/sec [132,532..136,254] → 144,229 ops/sec [142,913..145,731] 🟢 +7.0%
nested closures 48,930 ops/sec [48,589..49,210] → 68,938 ops/sec [68,197..70,039] 🟢 +40.9% 139,138 ops/sec [138,585..139,554] → 147,047 ops/sec [143,723..147,973] 🟢 +5.7%
function as argument 32,909 ops/sec [32,210..32,993] → 45,668 ops/sec [45,278..47,284] 🟢 +38.8% 143,186 ops/sec [140,214..145,920] → 155,743 ops/sec [152,049..158,512] 🟢 +8.8%
function returning function 42,625 ops/sec [42,234..43,572] → 60,122 ops/sec [59,504..61,491] 🟢 +41.0% 164,796 ops/sec [161,589..166,828] → 169,360 ops/sec [165,888..173,050] ~ overlap (+2.8%)
compose two functions 25,911 ops/sec [25,594..26,055] → 36,591 ops/sec [36,127..38,095] 🟢 +41.2% 91,135 ops/sec [90,192..91,335] → 99,226 ops/sec [95,374..99,989] 🟢 +8.9%
fn.call 59,641 ops/sec [59,209..59,727] → 82,329 ops/sec [81,381..85,651] 🟢 +38.0% 92,741 ops/sec [91,499..93,593] → 101,390 ops/sec [97,125..104,763] 🟢 +9.3%
fn.apply 47,308 ops/sec [47,137..47,445] → 65,409 ops/sec [64,206..65,826] 🟢 +38.3% 95,460 ops/sec [94,961..95,782] → 94,990 ops/sec [94,382..95,183] ~ overlap (-0.5%)
fn.bind 56,701 ops/sec [55,861..58,465] → 79,199 ops/sec [78,012..82,281] 🟢 +39.7% 166,102 ops/sec [164,328..166,775] → 173,008 ops/sec [171,275..175,476] 🟢 +4.2%
recursive sum to 50 3,742 ops/sec [3,660..3,836] → 5,503 ops/sec [5,366..5,733] 🟢 +47.1% 18,285 ops/sec [18,157..18,308] → 21,300 ops/sec [20,725..21,387] 🟢 +16.5%
recursive tree traversal 7,241 ops/sec [7,051..7,380] → 10,236 ops/sec [9,970..10,371] 🟢 +41.4% 18,052 ops/sec [17,884..18,294] → 21,135 ops/sec [20,663..21,301] 🟢 +17.1%
collections.js — Interp: 🟢 12 · avg +41.2% · Bytecode: 🟢 7, 5 unch. · avg +3.4%
Benchmark Interpreted Δ Bytecode Δ
add 50 elements 3,057 ops/sec [3,040..3,079] → 4,370 ops/sec [4,295..4,475] 🟢 +43.0% 3,866 ops/sec [3,801..3,971] → 4,058 ops/sec [3,999..4,086] 🟢 +5.0%
has lookup (50 elements) 45,467 ops/sec [45,212..46,812] → 64,955 ops/sec [64,532..65,818] 🟢 +42.9% 57,442 ops/sec [56,513..57,702] → 58,769 ops/sec [58,380..59,316] 🟢 +2.3%
delete elements 24,653 ops/sec [24,454..25,044] → 36,272 ops/sec [35,585..36,940] 🟢 +47.1% 29,456 ops/sec [29,042..29,667] → 30,472 ops/sec [30,362..30,724] 🟢 +3.4%
forEach iteration 5,013 ops/sec [4,842..5,245] → 7,355 ops/sec [7,316..7,429] 🟢 +46.7% 9,181 ops/sec [9,015..9,257] → 10,049 ops/sec [9,947..10,134] 🟢 +9.5%
spread to array 15,761 ops/sec [15,511..15,874] → 22,207 ops/sec [21,967..22,812] 🟢 +40.9% 109,923 ops/sec [109,163..111,075] → 114,397 ops/sec [113,142..116,347] 🟢 +4.1%
deduplicate array 20,797 ops/sec [20,264..21,252] → 29,703 ops/sec [29,533..29,883] 🟢 +42.8% 38,546 ops/sec [38,266..38,801] → 39,323 ops/sec [38,865..39,799] 🟢 +2.0%
set 50 entries 2,291 ops/sec [2,273..2,301] → 3,173 ops/sec [3,158..3,214] 🟢 +38.5% 2,993 ops/sec [2,956..3,020] → 3,029 ops/sec [2,999..3,060] ~ overlap (+1.2%)
get lookup (50 entries) 46,271 ops/sec [45,772..46,703] → 64,108 ops/sec [63,653..64,367] 🟢 +38.5% 51,977 ops/sec [51,631..52,433] → 52,523 ops/sec [52,298..53,038] ~ overlap (+1.1%)
has check 66,374 ops/sec [65,539..67,145] → 92,115 ops/sec [90,216..92,599] 🟢 +38.8% 75,946 ops/sec [75,510..76,676] → 76,170 ops/sec [74,423..77,233] ~ overlap (+0.3%)
delete entries 25,740 ops/sec [25,094..26,038] → 35,242 ops/sec [35,101..35,599] 🟢 +36.9% 27,607 ops/sec [27,535..28,036] → 27,455 ops/sec [26,721..28,786] ~ overlap (-0.6%)
forEach iteration 5,092 ops/sec [5,072..5,163] → 7,250 ops/sec [7,180..7,366] 🟢 +42.4% 9,242 ops/sec [9,117..9,315] → 10,309 ops/sec [10,186..10,527] 🟢 +11.6%
keys/values/entries 4,402 ops/sec [4,229..4,441] → 5,988 ops/sec [5,653..6,076] 🟢 +36.0% 14,874 ops/sec [14,682..15,352] → 15,074 ops/sec [13,917..15,539] ~ overlap (+1.3%)
csv.js — Interp: 🟢 13 · avg +41.9% · Bytecode: 🟢 12, 1 unch. · avg +8.6%
Benchmark Interpreted Δ Bytecode Δ
parse simple 3-column CSV 46,074 ops/sec [45,507..46,441] → 66,935 ops/sec [65,989..67,541] 🟢 +45.3% 49,404 ops/sec [48,922..49,739] → 54,340 ops/sec [53,993..54,565] 🟢 +10.0%
parse 10-row CSV 13,198 ops/sec [13,118..13,303] → 18,846 ops/sec [18,782..18,967] 🟢 +42.8% 13,363 ops/sec [13,151..13,489] → 15,307 ops/sec [15,185..15,530] 🟢 +14.5%
parse 100-row CSV 2,029 ops/sec [2,002..2,046] → 2,926 ops/sec [2,896..2,939] 🟢 +44.2% 2,155 ops/sec [2,123..2,187] → 2,383 ops/sec [2,304..2,495] 🟢 +10.6%
parse CSV with quoted fields 68,272 ops/sec [67,637..70,474] → 97,878 ops/sec [97,298..98,134] 🟢 +43.4% 74,090 ops/sec [72,837..75,494] → 80,856 ops/sec [80,459..81,427] 🟢 +9.1%
parse without headers (array of arrays) 5,701 ops/sec [5,532..5,758] → 7,930 ops/sec [7,794..8,427] 🟢 +39.1% 6,115 ops/sec [6,063..6,230] → 6,549 ops/sec [6,380..7,018] 🟢 +7.1%
parse with semicolon delimiter 9,420 ops/sec [9,261..9,672] → 13,803 ops/sec [13,485..14,384] 🟢 +46.5% 10,353 ops/sec [10,287..10,396] → 10,655 ops/sec [10,433..11,098] 🟢 +2.9%
stringify array of objects 66,481 ops/sec [64,865..67,453] → 97,747 ops/sec [94,055..101,116] 🟢 +47.0% 70,020 ops/sec [69,045..70,958] → 78,832 ops/sec [78,016..79,730] 🟢 +12.6%
stringify array of arrays 23,921 ops/sec [23,809..24,029] → 34,247 ops/sec [33,520..35,569] 🟢 +43.2% 23,930 ops/sec [23,545..24,026] → 27,259 ops/sec [27,163..27,307] 🟢 +13.9%
stringify with values needing escaping 48,774 ops/sec [48,214..49,748] → 71,435 ops/sec [70,868..71,669] 🟢 +46.5% 52,959 ops/sec [52,159..53,618] → 58,463 ops/sec [55,750..59,873] 🟢 +10.4%
reviver converts numbers 1,313 ops/sec [1,301..1,334] → 1,844 ops/sec [1,827..1,869] 🟢 +40.5% 1,621 ops/sec [1,594..1,633] → 1,685 ops/sec [1,641..1,736] 🟢 +3.9%
reviver filters empty to null 10,676 ops/sec [10,579..10,710] → 14,648 ops/sec [14,569..14,905] 🟢 +37.2% 13,594 ops/sec [13,364..14,373] → 13,938 ops/sec [13,348..14,780] ~ overlap (+2.5%)
parse then stringify 8,490 ops/sec [8,314..8,543] → 11,303 ops/sec [11,266..11,600] 🟢 +33.1% 8,501 ops/sec [8,311..8,611] → 8,893 ops/sec [8,838..9,538] 🟢 +4.6%
stringify then parse 8,141 ops/sec [7,955..8,275] → 11,062 ops/sec [10,885..11,230] 🟢 +35.9% 8,381 ops/sec [8,296..8,503] → 9,175 ops/sec [8,818..9,276] 🟢 +9.5%
destructuring.js — Interp: 🟢 22 · avg +39.1% · Bytecode: 🟢 11, 🔴 1, 10 unch. · avg +3.6%
Benchmark Interpreted Δ Bytecode Δ
simple array destructuring 164,820 ops/sec [162,076..166,948] → 228,664 ops/sec [222,552..234,057] 🟢 +38.7% 121,973 ops/sec [121,139..123,203] → 126,153 ops/sec [122,682..130,389] ~ overlap (+3.4%)
with rest element 112,628 ops/sec [108,855..115,061] → 158,651 ops/sec [155,568..161,186] 🟢 +40.9% 90,375 ops/sec [82,340..91,152] → 95,490 ops/sec [86,920..97,639] ~ overlap (+5.7%)
with defaults 164,470 ops/sec [163,527..164,867] → 229,123 ops/sec [225,701..238,740] 🟢 +39.3% 127,877 ops/sec [126,281..129,233] → 133,238 ops/sec [130,835..134,120] 🟢 +4.2%
skip elements 175,787 ops/sec [174,331..177,628] → 242,038 ops/sec [238,655..247,876] 🟢 +37.7% 132,748 ops/sec [131,412..133,289] → 136,690 ops/sec [132,788..137,697] ~ overlap (+3.0%)
nested array destructuring 83,442 ops/sec [82,925..85,382] → 115,331 ops/sec [114,087..118,712] 🟢 +38.2% 44,978 ops/sec [44,809..45,148] → 45,085 ops/sec [43,429..48,041] ~ overlap (+0.2%)
swap variables 196,274 ops/sec [195,223..197,496] → 274,199 ops/sec [268,516..280,487] 🟢 +39.7% 159,008 ops/sec [157,752..161,453] → 155,163 ops/sec [149,031..159,615] ~ overlap (-2.4%)
simple object destructuring 137,429 ops/sec [136,154..139,773] → 190,209 ops/sec [186,218..193,005] 🟢 +38.4% 158,343 ops/sec [157,542..160,083] → 173,120 ops/sec [172,933..174,364] 🟢 +9.3%
with defaults 155,274 ops/sec [155,002..155,458] → 217,032 ops/sec [209,270..223,679] 🟢 +39.8% 199,169 ops/sec [196,702..206,796] → 220,302 ops/sec [210,617..223,660] 🟢 +10.6%
with renaming 145,594 ops/sec [144,057..149,417] → 203,217 ops/sec [198,059..207,560] 🟢 +39.6% 153,797 ops/sec [150,408..157,674] → 170,016 ops/sec [164,636..173,354] 🟢 +10.5%
nested object destructuring 75,888 ops/sec [74,563..76,980] → 103,440 ops/sec [98,959..105,616] 🟢 +36.3% 82,772 ops/sec [79,116..84,139] → 86,849 ops/sec [85,787..86,945] 🟢 +4.9%
rest properties 54,210 ops/sec [53,425..54,946] → 74,824 ops/sec [73,813..76,684] 🟢 +38.0% 74,775 ops/sec [74,309..75,870] → 80,966 ops/sec [78,953..83,702] 🟢 +8.3%
object parameter 42,481 ops/sec [41,878..43,081] → 58,559 ops/sec [57,389..59,488] 🟢 +37.8% 67,900 ops/sec [66,239..69,283] → 71,203 ops/sec [70,606..72,621] 🟢 +4.9%
array parameter 51,657 ops/sec [50,880..53,010] → 70,900 ops/sec [70,107..73,321] 🟢 +37.3% 63,174 ops/sec [61,732..64,360] → 61,829 ops/sec [60,640..62,700] ~ overlap (-2.1%)
mixed destructuring in map 11,582 ops/sec [11,424..11,664] → 16,453 ops/sec [15,922..17,358] 🟢 +42.1% 17,694 ops/sec [17,477..17,783] → 18,804 ops/sec [18,473..18,837] 🟢 +6.3%
forEach with array destructuring 25,419 ops/sec [25,081..25,683] → 36,096 ops/sec [35,100..36,714] 🟢 +42.0% 23,590 ops/sec [23,173..24,183] → 22,949 ops/sec [22,607..24,063] ~ overlap (-2.7%)
map with array destructuring 26,804 ops/sec [26,033..27,180] → 37,304 ops/sec [36,194..38,439] 🟢 +39.2% 22,541 ops/sec [21,753..22,694] → 22,202 ops/sec [21,865..22,669] ~ overlap (-1.5%)
filter with array destructuring 26,660 ops/sec [26,268..27,008] → 38,605 ops/sec [38,190..38,947] 🟢 +44.8% 23,605 ops/sec [23,195..23,908] → 23,636 ops/sec [23,016..24,069] ~ overlap (+0.1%)
reduce with array destructuring 30,090 ops/sec [29,138..30,378] → 41,193 ops/sec [38,793..42,136] 🟢 +36.9% 25,246 ops/sec [24,918..25,609] → 24,288 ops/sec [24,083..25,060] ~ overlap (-3.8%)
map with object destructuring 26,246 ops/sec [25,614..26,566] → 35,982 ops/sec [35,407..37,023] 🟢 +37.1% 39,891 ops/sec [39,615..40,879] → 42,170 ops/sec [41,181..43,122] 🟢 +5.7%
map with nested destructuring 22,496 ops/sec [21,657..22,689] → 31,131 ops/sec [30,725..31,452] 🟢 +38.4% 36,013 ops/sec [35,280..37,263] → 38,967 ops/sec [38,493..39,244] 🟢 +8.2%
map with rest in destructuring 16,547 ops/sec [16,331..16,818] → 22,519 ops/sec [22,245..22,718] 🟢 +36.1% 12,568 ops/sec [12,481..12,663] → 12,185 ops/sec [11,988..12,255] 🔴 -3.1%
map with defaults in destructuring 20,281 ops/sec [19,943..21,174] → 28,680 ops/sec [28,342..28,879] 🟢 +41.4% 28,539 ops/sec [28,216..29,399] → 31,458 ops/sec [31,005..31,954] 🟢 +10.2%
fibonacci.js — Interp: 🟢 8 · avg +42.1% · Bytecode: 🟢 4, 4 unch. · avg +6.6%
Benchmark Interpreted Δ Bytecode Δ
recursive fib(15) 101 ops/sec [98..104] → 147 ops/sec [146..149] 🟢 +45.7% 540 ops/sec [535..555] → 589 ops/sec [585..595] 🟢 +9.2%
recursive fib(20) 9 ops/sec [9..9] → 13 ops/sec [13..14] 🟢 +46.1% 49 ops/sec [49..49] → 53 ops/sec [53..54] 🟢 +8.8%
recursive fib(15) typed 104 ops/sec [101..105] → 150 ops/sec [145..156] 🟢 +44.1% 560 ops/sec [547..567] → 612 ops/sec [596..619] 🟢 +9.2%
recursive fib(20) typed 9 ops/sec [9..9] → 13 ops/sec [13..14] 🟢 +44.6% 50 ops/sec [50..51] → 56 ops/sec [54..59] 🟢 +11.9%
iterative fib(20) via reduce 4,613 ops/sec [4,491..4,721] → 6,514 ops/sec [6,409..6,636] 🟢 +41.2% 9,919 ops/sec [9,883..9,923] → 10,307 ops/sec [9,798..10,390] ~ overlap (+3.9%)
iterator fib(20) 3,658 ops/sec [3,596..3,700] → 5,070 ops/sec [4,954..5,250] 🟢 +38.6% 6,359 ops/sec [6,259..6,713] → 6,860 ops/sec [6,634..6,892] ~ overlap (+7.9%)
iterator fib(20) via Iterator.from + take 4,984 ops/sec [4,878..4,991] → 6,822 ops/sec [6,708..6,850] 🟢 +36.9% 7,700 ops/sec [7,522..7,890] → 7,697 ops/sec [7,247..7,828] ~ overlap (-0.0%)
iterator fib(20) last value via reduce 3,807 ops/sec [3,759..3,859] → 5,323 ops/sec [5,282..5,332] 🟢 +39.8% 5,945 ops/sec [5,892..6,123] → 6,083 ops/sec [5,985..6,152] ~ overlap (+2.3%)
float16array.js — Interp: 🟢 32 · avg +33.2% · Bytecode: 🟢 11, 🔴 8, 13 unch. · avg -5.9%
Benchmark Interpreted Δ Bytecode Δ
new Float16Array(0) 117,274 ops/sec [115,008..119,134] → 158,511 ops/sec [157,089..158,754] 🟢 +35.2% 146,035 ops/sec [145,111..147,596] → 145,365 ops/sec [142,320..149,153] ~ overlap (-0.5%)
new Float16Array(100) 113,538 ops/sec [111,279..114,066] → 151,871 ops/sec [150,277..155,125] 🟢 +33.8% 137,952 ops/sec [137,117..139,097] → 140,685 ops/sec [140,257..141,352] 🟢 +2.0%
new Float16Array(1000) 95,514 ops/sec [94,563..95,913] → 125,310 ops/sec [124,230..126,476] 🟢 +31.2% 114,318 ops/sec [113,070..117,147] → 112,437 ops/sec [111,227..113,181] ~ overlap (-1.6%)
Float16Array.from([...100]) 4,961 ops/sec [4,830..5,000] → 6,445 ops/sec [6,387..6,485] 🟢 +29.9% 5,164 ops/sec [5,091..5,224] → 5,234 ops/sec [5,147..5,380] ~ overlap (+1.4%)
Float16Array.of(1.5, 2.5, 3.5, 4.5, 5.5) 127,257 ops/sec [126,534..127,591] → 172,935 ops/sec [171,474..174,019] 🟢 +35.9% 107,980 ops/sec [105,721..111,257] → 113,589 ops/sec [112,360..117,126] 🟢 +5.2%
new Float16Array(float64Array) 80,017 ops/sec [77,907..81,758] → 107,598 ops/sec [106,636..107,966] 🟢 +34.5% 89,976 ops/sec [89,051..90,641] → 94,792 ops/sec [93,960..96,141] 🟢 +5.4%
sequential write 100 elements 1,316 ops/sec [1,292..1,339] → 1,827 ops/sec [1,798..1,875] 🟢 +38.8% 4,671 ops/sec [4,571..4,702] → 4,608 ops/sec [4,515..4,644] ~ overlap (-1.4%)
sequential read 100 elements 1,496 ops/sec [1,473..1,504] → 2,002 ops/sec [1,997..2,060] 🟢 +33.8% 6,306 ops/sec [6,253..6,436] → 6,132 ops/sec [6,027..6,248] 🔴 -2.7%
write special values (NaN, Inf, -0) 63,837 ops/sec [62,195..64,885] → 87,931 ops/sec [85,205..90,536] 🟢 +37.7% 127,528 ops/sec [125,117..129,452] → 135,386 ops/sec [128,962..139,829] ~ overlap (+6.2%)
Float16Array write 1,373 ops/sec [1,361..1,384] → 1,856 ops/sec [1,839..1,891] 🟢 +35.2% 4,567 ops/sec [4,544..4,639] → 4,610 ops/sec [4,458..4,656] ~ overlap (+0.9%)
Float32Array write 1,323 ops/sec [1,313..1,357] → 1,881 ops/sec [1,837..1,921] 🟢 +42.2% 4,653 ops/sec [4,625..4,696] → 4,652 ops/sec [4,600..4,764] ~ overlap (-0.0%)
Float64Array write 1,346 ops/sec [1,342..1,365] → 1,860 ops/sec [1,785..1,913] 🟢 +38.2% 4,695 ops/sec [4,643..4,775] → 4,627 ops/sec [4,512..4,673] ~ overlap (-1.5%)
Float16Array read 1,459 ops/sec [1,451..1,469] → 1,995 ops/sec [1,985..2,026] 🟢 +36.8% 6,018 ops/sec [5,910..6,066] → 4,922 ops/sec [4,921..4,950] 🔴 -18.2%
Float32Array read 1,491 ops/sec [1,441..1,501] → 2,041 ops/sec [2,023..2,042] 🟢 +36.8% 6,306 ops/sec [6,242..6,399] → 6,283 ops/sec [6,228..6,297] ~ overlap (-0.4%)
Float64Array read 1,493 ops/sec [1,489..1,500] → 2,078 ops/sec [2,029..2,098] 🟢 +39.1% 6,324 ops/sec [6,250..6,453] → 6,449 ops/sec [6,312..6,509] ~ overlap (+2.0%)
fill(1.5) 21,687 ops/sec [21,400..21,911] → 30,867 ops/sec [30,594..31,652] 🟢 +42.3% 23,107 ops/sec [22,642..23,814] → 24,301 ops/sec [24,190..24,319] 🟢 +5.2%
slice() 86,308 ops/sec [86,045..86,485] → 110,140 ops/sec [109,618..111,119] 🟢 +27.6% 90,875 ops/sec [89,498..91,088] → 96,250 ops/sec [95,508..96,904] 🟢 +5.9%
map(x => x * 2) 2,481 ops/sec [2,429..2,503] → 3,495 ops/sec [3,446..3,519] 🟢 +40.9% 3,801 ops/sec [3,715..3,898] → 3,805 ops/sec [3,738..3,824] ~ overlap (+0.1%)
filter(x => x > 25) 2,584 ops/sec [2,529..2,650] → 3,562 ops/sec [3,517..3,601] 🟢 +37.9% 4,207 ops/sec [4,189..4,315] → 4,246 ops/sec [4,033..4,323] ~ overlap (+0.9%)
reduce (sum) 2,509 ops/sec [2,481..2,646] → 3,523 ops/sec [3,477..3,538] 🟢 +40.5% 3,594 ops/sec [3,510..3,748] → 3,416 ops/sec [3,334..3,493] 🔴 -4.9%
sort() 20,889 ops/sec [20,816..20,911] → 22,815 ops/sec [22,513..22,903] 🟢 +9.2% 23,802 ops/sec [23,127..24,025] → 9,146 ops/sec [9,127..9,213] 🔴 -61.6%
indexOf() 106,467 ops/sec [104,768..107,623] → 124,123 ops/sec [122,672..125,321] 🟢 +16.6% 132,347 ops/sec [131,010..132,977] → 63,523 ops/sec [63,169..63,865] 🔴 -52.0%
reverse() 106,203 ops/sec [105,131..107,799] → 145,521 ops/sec [145,015..146,044] 🟢 +37.0% 119,220 ops/sec [118,213..120,204] → 130,252 ops/sec [128,920..130,965] 🟢 +9.3%
toReversed() 54,619 ops/sec [53,518..55,358] → 63,748 ops/sec [63,314..63,986] 🟢 +16.7% 61,063 ops/sec [61,007..61,219] → 30,560 ops/sec [30,293..30,826] 🔴 -50.0%
toSorted() 819 ops/sec [804..824] → 900 ops/sec [895..902] 🟢 +9.9% 917 ops/sec [904..931] → 350 ops/sec [348..351] 🔴 -61.8%
create view over existing buffer 131,254 ops/sec [130,476..131,495] → 176,743 ops/sec [175,021..178,894] 🟢 +34.7% 159,943 ops/sec [155,003..161,032] → 168,534 ops/sec [165,953..171,154] 🟢 +5.4%
subarray() 176,847 ops/sec [174,599..180,824] → 242,980 ops/sec [241,530..245,423] 🟢 +37.4% 226,166 ops/sec [222,890..230,125] → 235,830 ops/sec [231,224..242,911] 🟢 +4.3%
set() from array 195,618 ops/sec [191,955..199,166] → 276,300 ops/sec [271,096..278,855] 🟢 +41.2% 248,914 ops/sec [245,078..250,918] → 270,793 ops/sec [268,730..273,555] 🟢 +8.8%
for-of loop 2,154 ops/sec [2,137..2,186] → 2,846 ops/sec [2,823..2,855] 🟢 +32.1% 9,050 ops/sec [8,986..9,147] → 7,905 ops/sec [7,494..8,140] 🔴 -12.7%
spread into array 8,822 ops/sec [8,346..8,882] → 11,678 ops/sec [11,486..12,057] 🟢 +32.4% 34,928 ops/sec [33,772..36,196] → 37,884 ops/sec [36,502..38,249] 🟢 +8.5%
f16round(1.337) 254,827 ops/sec [254,061..255,861] → 343,810 ops/sec [337,037..357,488] 🟢 +34.9% 244,143 ops/sec [242,607..244,549] → 261,022 ops/sec [259,752..262,124] 🟢 +6.9%
f16round over 100 values 1,520 ops/sec [1,482..1,551] → 2,004 ops/sec [1,990..2,022] 🟢 +31.8% 2,864 ops/sec [2,847..2,870] → 2,884 ops/sec [2,840..2,897] ~ overlap (+0.7%)
for-of.js — Interp: 🟢 7 · avg +36.7% · Bytecode: 7 unch. · avg +3.2%
Benchmark Interpreted Δ Bytecode Δ
for...of with 10-element array 19,067 ops/sec [18,923..19,208] → 26,552 ops/sec [26,115..26,864] 🟢 +39.3% 116,020 ops/sec [114,744..117,238] → 116,325 ops/sec [113,003..119,472] ~ overlap (+0.3%)
for...of with 100-element array 2,194 ops/sec [2,184..2,257] → 3,057 ops/sec [3,014..3,073] 🟢 +39.3% 14,674 ops/sec [14,518..14,805] → 14,719 ops/sec [14,437..16,247] ~ overlap (+0.3%)
for...of with string (10 chars) 13,964 ops/sec [13,715..14,039] → 19,357 ops/sec [18,695..19,595] 🟢 +38.6% 33,004 ops/sec [32,505..34,236] → 35,638 ops/sec [30,142..38,323] ~ overlap (+8.0%)
for...of with Set (10 elements) 19,823 ops/sec [19,728..19,945] → 26,391 ops/sec [25,859..27,528] 🟢 +33.1% 110,553 ops/sec [106,951..111,344] → 116,187 ops/sec [110,903..120,070] ~ overlap (+5.1%)
for...of with Map entries (10 entries) 13,256 ops/sec [13,136..13,433] → 17,667 ops/sec [17,581..18,284] 🟢 +33.3% 16,718 ops/sec [14,590..16,947] → 17,253 ops/sec [16,515..18,227] ~ overlap (+3.2%)
for...of with destructuring 16,564 ops/sec [16,338..16,723] → 22,567 ops/sec [22,200..23,017] 🟢 +36.2% 21,527 ops/sec [21,332..21,913] → 22,491 ops/sec [21,252..23,077] ~ overlap (+4.5%)
for-await-of with sync array 18,730 ops/sec [18,422..19,030] → 25,673 ops/sec [24,836..26,055] 🟢 +37.1% 17,859 ops/sec [17,665..17,959] → 18,036 ops/sec [17,621..18,628] ~ overlap (+1.0%)
generators.js — Interp: 🟢 4 · avg +41.6% · Bytecode: 🟢 4 · avg +9.0%
Benchmark Interpreted Δ Bytecode Δ
manual next over object generator 851 ops/sec [839..871] → 1,235 ops/sec [1,209..1,240] 🟢 +45.1% 1,106 ops/sec [1,087..1,108] → 1,181 ops/sec [1,146..1,226] 🟢 +6.8%
for...of over object generator 1,333 ops/sec [1,319..1,369] → 1,885 ops/sec [1,853..1,894] 🟢 +41.4% 2,110 ops/sec [2,074..2,165] → 2,318 ops/sec [2,247..2,370] 🟢 +9.9%
yield delegation 1,323 ops/sec [1,312..1,358] → 1,878 ops/sec [1,865..1,903] 🟢 +41.9% 2,158 ops/sec [2,137..2,181] → 2,353 ops/sec [2,336..2,387] 🟢 +9.0%
class generator method 1,345 ops/sec [1,327..1,384] → 1,854 ops/sec [1,840..1,899] 🟢 +37.9% 2,104 ops/sec [2,070..2,143] → 2,320 ops/sec [2,285..2,410] 🟢 +10.3%
iterators.js — Interp: 🟢 42 · avg +38.8% · Bytecode: 🟢 20, 🔴 4, 18 unch. · avg +2.4%
Benchmark Interpreted Δ Bytecode Δ
Iterator.from({next}).toArray() — 20 elements 4,522 ops/sec [4,413..4,595] → 6,217 ops/sec [6,204..6,398] 🟢 +37.5% 6,674 ops/sec [6,604..6,807] → 6,938 ops/sec [6,901..7,027] 🟢 +4.0%
Iterator.from({next}).toArray() — 50 elements 1,902 ops/sec [1,874..1,917] → 2,648 ops/sec [2,598..2,730] 🟢 +39.2% 3,040 ops/sec [2,974..3,056] → 3,164 ops/sec [3,106..3,226] 🟢 +4.1%
spread pre-wrapped iterator — 20 elements 4,456 ops/sec [4,406..4,533] → 6,242 ops/sec [6,175..6,348] 🟢 +40.1% 6,886 ops/sec [6,817..6,947] → 7,020 ops/sec [6,902..7,133] ~ overlap (+1.9%)
Iterator.from({next}).forEach — 50 elements 1,382 ops/sec [1,376..1,390] → 1,951 ops/sec [1,925..2,017] 🟢 +41.2% 2,272 ops/sec [2,232..2,312] → 2,389 ops/sec [2,373..2,427] 🟢 +5.2%
Iterator.from({next}).reduce — 50 elements 1,410 ops/sec [1,398..1,425] → 1,973 ops/sec [1,960..1,978] 🟢 +39.9% 2,202 ops/sec [2,175..2,224] → 2,307 ops/sec [2,278..2,333] 🟢 +4.8%
wrap array iterator 71,489 ops/sec [70,697..71,888] → 104,186 ops/sec [96,558..104,581] 🟢 +45.7% 76,966 ops/sec [75,430..77,407] → 81,079 ops/sec [80,494..81,330] 🟢 +5.3%
wrap plain {next()} object 3,100 ops/sec [3,070..3,175] → 4,383 ops/sec [4,291..4,461] 🟢 +41.4% 4,851 ops/sec [4,792..4,920] → 4,944 ops/sec [4,898..4,966] ~ overlap (+1.9%)
map + toArray (50 elements) 1,406 ops/sec [1,397..1,435] → 1,990 ops/sec [1,977..2,028] 🟢 +41.5% 2,280 ops/sec [2,266..2,297] → 2,347 ops/sec [2,314..2,387] 🟢 +2.9%
filter + toArray (50 elements) 1,421 ops/sec [1,394..1,434] → 1,979 ops/sec [1,963..2,005] 🟢 +39.2% 2,224 ops/sec [2,195..2,240] → 2,415 ops/sec [2,360..2,503] 🟢 +8.6%
take(10) + toArray (50 element source) 8,688 ops/sec [8,544..8,877] → 12,324 ops/sec [12,243..12,380] 🟢 +41.9% 12,991 ops/sec [12,852..13,176] → 13,085 ops/sec [12,619..13,209] ~ overlap (+0.7%)
drop(40) + toArray (50 element source) 1,927 ops/sec [1,919..1,946] → 2,865 ops/sec [2,734..3,002] 🟢 +48.7% 2,982 ops/sec [2,951..3,028] → 3,135 ops/sec [3,118..3,231] 🟢 +5.1%
chained map + filter + take (100 element source) 2,710 ops/sec [2,701..2,722] → 3,915 ops/sec [3,895..3,929] 🟢 +44.5% 4,276 ops/sec [4,238..4,298] → 4,442 ops/sec [4,386..4,451] 🟢 +3.9%
some + every (50 elements) 802 ops/sec [799..805] → 1,185 ops/sec [1,182..1,197] 🟢 +47.8% 1,300 ops/sec [1,283..1,310] → 1,378 ops/sec [1,357..1,396] 🟢 +6.0%
find (50 elements) 1,789 ops/sec [1,770..1,805] → 2,777 ops/sec [2,439..2,784] 🟢 +55.3% 2,842 ops/sec [2,825..2,846] → 2,935 ops/sec [2,906..2,986] 🟢 +3.3%
concat 2 arrays (10 + 10 elements) 67,916 ops/sec [66,937..69,070] → 95,616 ops/sec [92,203..96,426] 🟢 +40.8% 74,698 ops/sec [73,733..75,514] → 78,388 ops/sec [76,913..79,202] 🟢 +4.9%
concat 5 arrays (10 elements each) 41,063 ops/sec [39,915..41,606] → 56,483 ops/sec [55,462..57,694] 🟢 +37.5% 45,509 ops/sec [45,085..45,788] → 46,338 ops/sec [45,192..47,414] ~ overlap (+1.8%)
concat 2 arrays (20 + 20 elements) 59,592 ops/sec [58,378..60,196] → 83,395 ops/sec [82,051..83,811] 🟢 +39.9% 64,778 ops/sec [63,506..65,118] → 67,247 ops/sec [65,474..70,115] 🟢 +3.8%
concat + filter + toArray (20 + 20 elements) 5,834 ops/sec [5,759..5,876] → 8,371 ops/sec [8,275..8,420] 🟢 +43.5% 9,591 ops/sec [9,480..9,631] → 10,177 ops/sec [10,009..10,460] 🟢 +6.1%
concat + map + take (20 + 20 elements, take 10) 18,335 ops/sec [18,223..18,634] → 25,564 ops/sec [25,031..26,575] 🟢 +39.4% 26,810 ops/sec [26,454..27,324] → 28,691 ops/sec [27,818..28,848] 🟢 +7.0%
concat Sets (15 + 15 elements) 67,925 ops/sec [66,949..69,319] → 91,565 ops/sec [90,945..99,654] 🟢 +34.8% 70,971 ops/sec [70,447..71,184] → 74,454 ops/sec [72,438..75,416] 🟢 +4.9%
concat strings (13 + 13 characters) 49,050 ops/sec [48,313..49,870] → 65,121 ops/sec [64,293..69,870] 🟢 +32.8% 47,245 ops/sec [46,772..47,904] → 50,429 ops/sec [49,256..50,692] 🟢 +6.7%
zip 2 arrays (10 + 10 elements) 28,033 ops/sec [27,386..28,463] → 37,753 ops/sec [36,976..38,287] 🟢 +34.7% 28,967 ops/sec [28,631..29,052] → 29,518 ops/sec [28,464..30,122] ~ overlap (+1.9%)
zip 3 arrays (10 elements each) 25,530 ops/sec [24,757..25,809] → 34,764 ops/sec [34,046..35,801] 🟢 +36.2% 27,152 ops/sec [26,617..27,528] → 26,284 ops/sec [25,863..26,916] ~ overlap (-3.2%)
zip 2 arrays (20 + 20 elements) 18,839 ops/sec [18,462..19,109] → 25,207 ops/sec [24,991..25,575] 🟢 +33.8% 19,382 ops/sec [19,333..19,549] → 19,513 ops/sec [18,983..20,058] ~ overlap (+0.7%)
zip 2 arrays (50 + 50 elements) 9,538 ops/sec [9,390..9,740] → 12,799 ops/sec [12,544..13,100] 🟢 +34.2% 9,782 ops/sec [9,614..9,853] → 9,474 ops/sec [9,175..9,663] ~ overlap (-3.2%)
zip shortest mode (20 + 10 elements) 27,384 ops/sec [26,931..27,460] → 37,312 ops/sec [36,948..37,697] 🟢 +36.3% 28,555 ops/sec [28,280..29,625] → 28,536 ops/sec [27,992..29,167] ~ overlap (-0.1%)
zip longest mode (10 + 20 elements) 16,331 ops/sec [16,158..16,852] → 22,230 ops/sec [22,076..22,261] 🟢 +36.1% 17,222 ops/sec [17,192..17,270] → 16,673 ops/sec [16,513..16,762] 🔴 -3.2%
zip strict mode (20 + 20 elements) 17,724 ops/sec [17,584..17,951] → 23,432 ops/sec [23,287..24,223] 🟢 +32.2% 18,303 ops/sec [18,191..18,496] → 16,911 ops/sec [12,985..17,806] 🔴 -7.6%
zip + map + toArray (20 + 20 elements) 7,291 ops/sec [7,275..7,421] → 9,867 ops/sec [9,749..9,913] 🟢 +35.3% 5,783 ops/sec [5,683..5,802] → 5,800 ops/sec [5,720..5,831] ~ overlap (+0.3%)
zip + filter + toArray (20 + 20 elements) 6,954 ops/sec [6,930..7,031] → 9,591 ops/sec [9,565..9,718] 🟢 +37.9% 5,698 ops/sec [5,644..5,751] → 5,878 ops/sec [5,794..5,989] 🟢 +3.2%
zip Sets (15 + 15 elements) 23,317 ops/sec [23,160..23,838] → 30,375 ops/sec [30,112..30,851] 🟢 +30.3% 23,501 ops/sec [23,381..23,633] → 22,844 ops/sec [22,255..23,196] 🔴 -2.8%
zipKeyed 2 keys (10 elements each) 28,609 ops/sec [28,433..29,146] → 38,310 ops/sec [37,639..38,645] 🟢 +33.9% 29,117 ops/sec [28,802..29,540] → 29,377 ops/sec [28,929..29,914] ~ overlap (+0.9%)
zipKeyed 3 keys (20 elements each) 14,661 ops/sec [14,546..14,753] → 19,287 ops/sec [18,511..20,029] 🟢 +31.6% 14,802 ops/sec [14,439..15,085] → 15,015 ops/sec [14,813..15,242] ~ overlap (+1.4%)
zipKeyed longest mode (10 + 20 elements) 16,747 ops/sec [16,407..16,907] → 22,254 ops/sec [22,072..23,167] 🟢 +32.9% 16,571 ops/sec [16,332..16,774] → 17,032 ops/sec [16,616..17,373] ~ overlap (+2.8%)
zipKeyed strict mode (20 + 20 elements) 17,241 ops/sec [16,998..17,521] → 23,303 ops/sec [22,841..24,611] 🟢 +35.2% 17,565 ops/sec [17,448..17,640] → 17,694 ops/sec [17,326..17,930] ~ overlap (+0.7%)
zipKeyed + filter + map (20 elements) 5,134 ops/sec [5,117..5,204] → 7,222 ops/sec [7,057..7,275] 🟢 +40.7% 6,976 ops/sec [6,823..7,001] → 6,981 ops/sec [6,857..7,184] ~ overlap (+0.1%)
array.values().map().filter().toArray() 2,594 ops/sec [2,548..2,616] → 3,789 ops/sec [3,736..3,828] 🟢 +46.0% 4,569 ops/sec [4,479..4,642] → 4,871 ops/sec [4,760..4,962] 🟢 +6.6%
array.values().take(5).toArray() 94,066 ops/sec [93,450..95,162] → 125,318 ops/sec [124,269..127,029] 🟢 +33.2% 106,573 ops/sec [102,628..108,297] → 109,519 ops/sec [107,396..113,045] ~ overlap (+2.8%)
array.values().drop(45).toArray() 75,925 ops/sec [75,241..76,576] → 102,007 ops/sec [99,282..103,080] 🟢 +34.4% 83,652 ops/sec [82,591..84,020] → 87,926 ops/sec [85,182..89,488] 🟢 +5.1%
map.entries() chained helpers 3,882 ops/sec [3,786..3,922] → 5,309 ops/sec [5,298..5,330] 🟢 +36.8% 3,072 ops/sec [3,049..3,086] → 2,944 ops/sec [2,898..2,967] 🔴 -4.2%
set.values() chained helpers 5,989 ops/sec [5,850..6,099] → 8,547 ops/sec [8,287..8,987] 🟢 +42.7% 9,837 ops/sec [9,725..9,914] → 10,482 ops/sec [9,598..10,981] ~ overlap (+6.6%)
string iterator map + toArray 5,085 ops/sec [5,004..5,180] → 7,221 ops/sec [7,115..7,489] 🟢 +42.0% 5,818 ops/sec [5,743..5,970] → 5,809 ops/sec [5,636..5,891] ~ overlap (-0.2%)
json.js — Interp: 🟢 20 · avg +43.8% · Bytecode: 🟢 15, 5 unch. · avg +7.9%
Benchmark Interpreted Δ Bytecode Δ
parse simple object 68,938 ops/sec [67,235..71,140] → 99,753 ops/sec [98,964..100,838] 🟢 +44.7% 75,254 ops/sec [71,445..76,723] → 83,208 ops/sec [82,366..83,932] 🟢 +10.6%
parse nested object 46,359 ops/sec [44,625..47,051] → 67,365 ops/sec [66,150..69,377] 🟢 +45.3% 49,146 ops/sec [48,924..49,366] → 56,063 ops/sec [55,791..56,302] 🟢 +14.1%
parse array of objects 27,816 ops/sec [27,097..28,473] → 41,320 ops/sec [39,357..42,563] 🟢 +48.5% 30,012 ops/sec [29,676..30,319] → 32,261 ops/sec [32,072..32,871] 🟢 +7.5%
parse large flat object 29,281 ops/sec [28,424..31,827] → 44,758 ops/sec [43,516..46,780] 🟢 +52.9% 32,202 ops/sec [31,961..32,600] → 36,807 ops/sec [35,381..37,935] 🟢 +14.3%
parse mixed types 34,473 ops/sec [34,221..35,741] → 51,978 ops/sec [50,294..54,379] 🟢 +50.8% 38,927 ops/sec [38,583..39,439] → 41,965 ops/sec [39,270..42,774] ~ overlap (+7.8%)
stringify simple object 74,863 ops/sec [72,609..75,542] → 107,585 ops/sec [105,671..114,807] 🟢 +43.7% 79,879 ops/sec [78,737..80,387] → 82,169 ops/sec [79,683..83,230] ~ overlap (+2.9%)
stringify nested object 43,057 ops/sec [42,377..43,846] → 63,650 ops/sec [62,833..64,075] 🟢 +47.8% 43,219 ops/sec [41,014..44,216] → 46,822 ops/sec [46,305..47,507] 🟢 +8.3%
stringify array of objects 19,067 ops/sec [18,681..19,274] → 28,395 ops/sec [28,262..29,082] 🟢 +48.9% 19,245 ops/sec [19,127..19,921] → 21,693 ops/sec [21,191..23,208] 🟢 +12.7%
stringify mixed types 30,217 ops/sec [29,867..30,386] → 42,523 ops/sec [42,259..42,615] 🟢 +40.7% 28,731 ops/sec [28,267..29,005] → 31,474 ops/sec [31,167..31,972] 🟢 +9.5%
reviver doubles numbers 14,251 ops/sec [14,187..14,308] → 20,502 ops/sec [20,401..20,749] 🟢 +43.9% 18,941 ops/sec [18,641..19,952] → 20,190 ops/sec [19,990..20,212] 🟢 +6.6%
reviver filters properties 13,375 ops/sec [13,160..13,459] → 19,425 ops/sec [19,018..19,808] 🟢 +45.2% 16,669 ops/sec [16,170..17,116] → 16,957 ops/sec [16,675..17,603] ~ overlap (+1.7%)
reviver on nested object 16,482 ops/sec [16,415..16,521] → 23,726 ops/sec [23,689..23,947] 🟢 +43.9% 20,679 ops/sec [20,628..20,784] → 21,774 ops/sec [21,377..22,291] 🟢 +5.3%
reviver on array 9,002 ops/sec [8,623..9,062] → 12,452 ops/sec [12,156..12,545] 🟢 +38.3% 12,352 ops/sec [12,112..12,419] → 12,458 ops/sec [12,249..12,789] ~ overlap (+0.9%)
replacer function doubles numbers 16,947 ops/sec [16,871..17,024] → 23,767 ops/sec [23,063..24,004] 🟢 +40.2% 23,265 ops/sec [23,000..23,498] → 24,701 ops/sec [23,455..24,821] ~ overlap (+6.2%)
replacer function excludes properties 22,661 ops/sec [22,288..22,773] → 30,442 ops/sec [29,979..31,718] 🟢 +34.3% 28,500 ops/sec [28,179..28,895] → 29,158 ops/sec [29,025..30,084] 🟢 +2.3%
array replacer (allowlist) 50,781 ops/sec [47,214..51,345] → 65,853 ops/sec [65,023..67,947] 🟢 +29.7% 46,119 ops/sec [45,570..47,981] → 49,874 ops/sec [49,208..50,492] 🟢 +8.1%
stringify with 2-space indent 37,225 ops/sec [36,493..37,501] → 53,565 ops/sec [52,557..55,861] 🟢 +43.9% 39,215 ops/sec [38,319..39,657] → 42,096 ops/sec [41,279..42,668] 🟢 +7.3%
stringify with tab indent 38,166 ops/sec [37,172..38,472] → 54,148 ops/sec [53,011..55,400] 🟢 +41.9% 37,820 ops/sec [37,735..37,952] → 41,565 ops/sec [41,343..41,689] 🟢 +9.9%
parse then stringify 22,774 ops/sec [22,687..22,869] → 33,967 ops/sec [32,380..34,398] 🟢 +49.1% 24,126 ops/sec [23,381..24,564] → 27,468 ops/sec [27,173..27,967] 🟢 +13.9%
stringify then parse 13,714 ops/sec [13,628..14,108] → 19,542 ops/sec [19,105..21,153] 🟢 +42.5% 14,586 ops/sec [14,470..14,616] → 15,850 ops/sec [15,709..15,923] 🟢 +8.7%
jsx.jsx — Interp: 🟢 21 · avg +31.0% · Bytecode: 🟢 10, 11 unch. · avg +4.2%
Benchmark Interpreted Δ Bytecode Δ
simple element 92,317 ops/sec [90,940..94,367] → 122,280 ops/sec [121,185..123,449] 🟢 +32.5% 141,698 ops/sec [140,393..145,444] → 147,933 ops/sec [146,819..149,018] 🟢 +4.4%
self-closing element 94,791 ops/sec [92,916..95,316] → 125,289 ops/sec [124,896..125,600] 🟢 +32.2% 156,971 ops/sec [148,880..160,913] → 164,439 ops/sec [160,357..166,244] ~ overlap (+4.8%)
element with string attribute 79,140 ops/sec [77,253..79,591] → 105,992 ops/sec [103,889..107,279] 🟢 +33.9% 114,572 ops/sec [113,382..115,154] → 118,020 ops/sec [116,755..120,733] 🟢 +3.0%
element with multiple attributes 69,974 ops/sec [68,487..71,165] → 91,735 ops/sec [90,337..92,425] 🟢 +31.1% 85,095 ops/sec [84,685..87,849] → 91,100 ops/sec [90,707..91,825] 🟢 +7.1%
element with expression attribute 74,163 ops/sec [72,734..75,416] → 96,605 ops/sec [95,668..100,441] 🟢 +30.3% 121,988 ops/sec [120,189..125,031] → 130,340 ops/sec [122,664..135,536] ~ overlap (+6.8%)
text child 93,262 ops/sec [91,861..94,384] → 122,515 ops/sec [117,316..125,926] 🟢 +31.4% 141,745 ops/sec [134,931..147,913] → 154,829 ops/sec [148,367..157,244] 🟢 +9.2%
expression child 89,402 ops/sec [89,211..90,146] → 119,214 ops/sec [117,812..121,435] 🟢 +33.3% 136,277 ops/sec [135,394..139,704] → 140,068 ops/sec [139,004..140,258] ~ overlap (+2.8%)
mixed text and expression 84,585 ops/sec [83,937..85,891] → 112,614 ops/sec [110,079..113,413] 🟢 +33.1% 118,724 ops/sec [115,182..121,642] → 129,766 ops/sec [126,107..132,798] 🟢 +9.3%
nested elements (3 levels) 35,545 ops/sec [35,150..35,660] → 47,648 ops/sec [46,653..48,012] 🟢 +34.1% 54,435 ops/sec [54,121..55,255] → 62,245 ops/sec [60,829..62,505] 🟢 +14.3%
sibling children 26,885 ops/sec [26,391..27,048] → 34,987 ops/sec [34,586..35,163] 🟢 +30.1% 39,443 ops/sec [39,083..41,380] → 40,443 ops/sec [39,946..40,556] ~ overlap (+2.5%)
component element 69,637 ops/sec [69,221..70,144] → 90,755 ops/sec [89,327..92,303] 🟢 +30.3% 108,138 ops/sec [105,871..110,829] → 106,869 ops/sec [105,905..108,234] ~ overlap (-1.2%)
component with children 42,833 ops/sec [41,863..43,252] → 55,389 ops/sec [54,699..56,939] 🟢 +29.3% 63,954 ops/sec [63,493..64,625] → 67,205 ops/sec [65,308..68,394] 🟢 +5.1%
dotted component 60,004 ops/sec [59,131..60,890] → 77,447 ops/sec [76,185..77,919] 🟢 +29.1% 82,944 ops/sec [82,206..83,564] → 83,891 ops/sec [81,392..85,550] ~ overlap (+1.1%)
empty fragment 94,888 ops/sec [94,601..97,214] → 126,132 ops/sec [124,951..128,226] 🟢 +32.9% 171,552 ops/sec [169,901..176,703] → 176,637 ops/sec [175,631..178,145] ~ overlap (+3.0%)
fragment with children 26,840 ops/sec [26,342..27,201] → 35,308 ops/sec [34,765..35,792] 🟢 +31.6% 40,254 ops/sec [40,186..40,510] → 42,078 ops/sec [41,410..42,321] 🟢 +4.5%
spread attributes 51,602 ops/sec [51,265..51,968] → 65,736 ops/sec [65,571..67,210] 🟢 +27.4% 58,842 ops/sec [57,779..59,785] → 63,416 ops/sec [62,325..65,980] 🟢 +7.8%
spread with overrides 46,223 ops/sec [44,203..46,575] → 60,053 ops/sec [58,941..60,940] 🟢 +29.9% 52,070 ops/sec [51,034..53,725] → 55,707 ops/sec [54,933..56,353] 🟢 +7.0%
shorthand props 73,031 ops/sec [72,972..73,491] → 95,143 ops/sec [94,815..95,994] 🟢 +30.3% 95,285 ops/sec [92,987..96,295] → 95,809 ops/sec [89,909..99,253] ~ overlap (+0.6%)
nav bar structure 12,902 ops/sec [12,814..12,983] → 16,728 ops/sec [16,691..16,825] 🟢 +29.7% 19,300 ops/sec [19,115..19,445] → 18,934 ops/sec [18,731..19,485] ~ overlap (-1.9%)
card component tree 15,345 ops/sec [15,119..15,496] → 19,611 ops/sec [19,166..19,752] 🟢 +27.8% 20,739 ops/sec [20,613..20,762] → 20,281 ops/sec [20,082..20,812] ~ overlap (-2.2%)
10 list items via Array.from 6,794 ops/sec [6,755..6,811] → 8,913 ops/sec [8,803..9,070] 🟢 +31.2% 9,155 ops/sec [8,962..9,233] → 9,104 ops/sec [8,965..9,138] ~ overlap (-0.6%)
modules.js — Interp: 🟢 9 · avg +39.2% · Bytecode: 🟢 8, 1 unch. · avg +8.0%
Benchmark Interpreted Δ Bytecode Δ
call imported function 151,656 ops/sec [147,326..158,111] → 217,628 ops/sec [209,131..219,646] 🟢 +43.5% 662,500 ops/sec [659,094..684,185] → 720,367 ops/sec [704,382..738,018] 🟢 +8.7%
call two imported functions 84,955 ops/sec [83,000..87,626] → 122,573 ops/sec [121,611..124,604] 🟢 +44.3% 426,525 ops/sec [421,417..428,868] → 487,887 ops/sec [479,941..489,489] 🟢 +14.4%
read imported constant 498,842 ops/sec [479,695..523,808] → 670,194 ops/sec [665,099..684,346] 🟢 +34.3% 1,625,027 ops/sec [1,601,931..1,640,557] → 1,807,618 ops/sec [1,798,861..1,809,772] 🟢 +11.2%
read imported string 488,440 ops/sec [480,104..509,094] → 659,716 ops/sec [642,445..685,529] 🟢 +35.1% 1,600,360 ops/sec [1,591,566..1,669,704] → 1,727,621 ops/sec [1,685,875..1,776,080] 🟢 +8.0%
read JSON string property 505,416 ops/sec [504,530..517,276] → 697,805 ops/sec [687,086..700,957] 🟢 +38.1% 1,657,893 ops/sec [1,624,247..1,685,479] → 1,737,868 ops/sec [1,706,743..1,783,853] 🟢 +4.8%
read JSON number property 487,917 ops/sec [480,736..501,381] → 672,077 ops/sec [652,089..700,963] 🟢 +37.7% 1,689,179 ops/sec [1,651,098..1,712,285] → 1,741,661 ops/sec [1,728,934..1,799,146] 🟢 +3.1%
read JSON boolean property 497,013 ops/sec [492,618..500,432] → 691,705 ops/sec [654,771..695,583] 🟢 +39.2% 1,686,749 ops/sec [1,625,203..1,711,843] → 1,742,953 ops/sec [1,674,905..1,790,077] ~ overlap (+3.3%)
read JSON array property 490,517 ops/sec [487,089..507,781] → 687,277 ops/sec [653,563..696,439] 🟢 +40.1% 1,615,750 ops/sec [1,600,214..1,632,048] → 1,744,956 ops/sec [1,725,037..1,749,674] 🟢 +8.0%
read multiple JSON properties 295,922 ops/sec [288,906..306,648] → 416,965 ops/sec [398,956..429,086] 🟢 +40.9% 1,336,572 ops/sec [1,334,127..1,338,340] → 1,470,829 ops/sec [1,456,139..1,478,946] 🟢 +10.0%
numbers.js — Interp: 🟢 11 · avg +42.6% · Bytecode: 🟢 11 · avg +12.4%
Benchmark Interpreted Δ Bytecode Δ
integer arithmetic 160,118 ops/sec [158,825..161,481] → 230,960 ops/sec [226,687..232,250] 🟢 +44.2% 708,042 ops/sec [698,937..717,143] → 773,187 ops/sec [767,690..775,499] 🟢 +9.2%
floating point arithmetic 187,256 ops/sec [184,452..188,046] → 268,353 ops/sec [264,923..280,748] 🟢 +43.3% 301,625 ops/sec [300,302..302,858] → 327,478 ops/sec [325,908..328,531] 🟢 +8.6%
number coercion 70,602 ops/sec [69,354..70,827] → 103,685 ops/sec [100,927..104,726] 🟢 +46.9% 88,706 ops/sec [87,761..88,917] → 104,183 ops/sec [103,727..104,322] 🟢 +17.4%
toFixed 41,583 ops/sec [41,186..41,870] → 58,945 ops/sec [57,683..61,062] 🟢 +41.8% 41,212 ops/sec [41,142..41,266] → 45,553 ops/sec [44,662..45,666] 🟢 +10.5%
toString 62,815 ops/sec [62,175..63,175] → 88,474 ops/sec [86,723..89,904] 🟢 +40.8% 70,111 ops/sec [69,689..71,194] → 76,642 ops/sec [71,593..78,963] 🟢 +9.3%
valueOf 92,934 ops/sec [92,172..97,030] → 132,062 ops/sec [130,309..134,304] 🟢 +42.1% 104,661 ops/sec [104,498..104,934] → 113,706 ops/sec [105,877..116,308] 🟢 +8.6%
toPrecision 37,160 ops/sec [36,993..37,332] → 51,332 ops/sec [50,905..52,763] 🟢 +38.1% 35,215 ops/sec [35,005..35,340] → 39,804 ops/sec [38,810..41,099] 🟢 +13.0%
Number.isNaN 112,394 ops/sec [110,785..114,291] → 161,109 ops/sec [157,097..166,537] 🟢 +43.3% 113,297 ops/sec [109,881..113,634] → 133,888 ops/sec [130,875..135,802] 🟢 +18.2%
Number.isFinite 110,810 ops/sec [109,887..111,456] → 157,484 ops/sec [145,478..166,589] 🟢 +42.1% 97,696 ops/sec [96,508..98,744] → 111,767 ops/sec [105,664..116,015] 🟢 +14.4%
Number.isInteger 115,006 ops/sec [113,056..116,784] → 166,344 ops/sec [164,699..166,937] 🟢 +44.6% 103,897 ops/sec [103,524..104,288] → 120,876 ops/sec [116,590..125,383] 🟢 +16.3%
Number.parseInt and parseFloat 89,316 ops/sec [88,090..89,645] → 126,588 ops/sec [125,054..131,310] 🟢 +41.7% 82,170 ops/sec [81,087..83,464] → 91,299 ops/sec [88,863..94,629] 🟢 +11.1%
objects.js — Interp: 🟢 7 · avg +35.5% · Bytecode: 🟢 5, 2 unch. · avg +4.7%
Benchmark Interpreted Δ Bytecode Δ
create simple object 205,901 ops/sec [203,635..206,350] → 279,095 ops/sec [269,427..293,259] 🟢 +35.5% 253,724 ops/sec [241,459..255,293] → 269,932 ops/sec [269,195..271,099] 🟢 +6.4%
create nested object 110,172 ops/sec [107,343..111,534] → 147,065 ops/sec [144,082..153,326] 🟢 +33.5% 109,294 ops/sec [108,258..110,057] → 115,643 ops/sec [114,973..115,884] 🟢 +5.8%
create 50 objects via Array.from 3,883 ops/sec [3,848..3,906] → 5,366 ops/sec [5,273..5,434] 🟢 +38.2% 4,513 ops/sec [4,345..4,615] → 4,512 ops/sec [4,225..4,732] ~ overlap (-0.0%)
property read 188,057 ops/sec [186,937..188,860] → 269,771 ops/sec [254,387..276,243] 🟢 +43.5% 289,162 ops/sec [285,340..290,682] → 314,552 ops/sec [312,351..316,473] 🟢 +8.8%
Object.keys 123,477 ops/sec [121,233..125,710] → 168,220 ops/sec [164,947..169,086] 🟢 +36.2% 139,516 ops/sec [138,058..140,698] → 146,617 ops/sec [145,674..147,475] 🟢 +5.1%
Object.entries 50,160 ops/sec [49,285..51,072] → 66,612 ops/sec [64,438..67,707] 🟢 +32.8% 53,598 ops/sec [52,235..55,314] → 54,615 ops/sec [52,924..55,579] ~ overlap (+1.9%)
spread operator 89,803 ops/sec [88,812..90,260] → 115,410 ops/sec [113,313..118,933] 🟢 +28.5% 98,097 ops/sec [95,720..101,396] → 103,085 ops/sec [102,378..105,285] 🟢 +5.1%
promises.js — Interp: 🟢 12 · avg +34.4% · Bytecode: 🟢 8, 4 unch. · avg +5.4%
Benchmark Interpreted Δ Bytecode Δ
Promise.resolve(value) 196,263 ops/sec [189,162..197,374] → 271,728 ops/sec [266,494..273,587] 🟢 +38.5% 213,065 ops/sec [210,487..215,943] → 228,797 ops/sec [225,998..237,974] 🟢 +7.4%
new Promise(resolve => resolve(value)) 76,570 ops/sec [75,647..78,446] → 102,586 ops/sec [101,664..107,222] 🟢 +34.0% 105,363 ops/sec [104,633..106,718] → 108,099 ops/sec [102,121..109,964] ~ overlap (+2.6%)
Promise.reject(reason) 202,749 ops/sec [197,386..203,954] → 275,136 ops/sec [273,319..279,387] 🟢 +35.7% 220,310 ops/sec [214,062..224,622] → 244,279 ops/sec [242,178..245,629] 🟢 +10.9%
resolve + then (1 handler) 73,841 ops/sec [73,589..74,379] → 100,003 ops/sec [98,147..100,295] 🟢 +35.4% 89,327 ops/sec [88,166..92,370] → 96,583 ops/sec [93,220..98,441] 🟢 +8.1%
resolve + then chain (3 deep) 30,115 ops/sec [29,791..30,264] → 39,448 ops/sec [34,705..40,621] 🟢 +31.0% 38,437 ops/sec [37,831..39,287] → 39,754 ops/sec [35,323..40,797] ~ overlap (+3.4%)
resolve + then chain (10 deep) 9,938 ops/sec [9,751..9,973] → 13,291 ops/sec [13,049..13,405] 🟢 +33.7% 12,829 ops/sec [12,434..13,062] → 13,296 ops/sec [13,090..13,522] 🟢 +3.6%
reject + catch + then 42,733 ops/sec [42,569..43,607] → 57,471 ops/sec [55,999..58,760] 🟢 +34.5% 51,443 ops/sec [50,158..52,889] → 54,725 ops/sec [53,802..55,964] 🟢 +6.4%
resolve + finally + then 37,691 ops/sec [37,018..38,314] → 50,515 ops/sec [49,965..51,332] 🟢 +34.0% 43,550 ops/sec [43,115..44,739] → 47,487 ops/sec [46,742..47,971] 🟢 +9.0%
Promise.all (5 resolved) 15,234 ops/sec [14,756..15,400] → 20,308 ops/sec [19,733..20,376] 🟢 +33.3% 15,521 ops/sec [15,349..15,854] → 16,094 ops/sec [15,872..16,435] 🟢 +3.7%
Promise.race (5 resolved) 16,305 ops/sec [16,006..16,536] → 22,084 ops/sec [21,272..23,366] 🟢 +35.4% 16,259 ops/sec [16,179..16,338] → 17,306 ops/sec [16,808..17,598] 🟢 +6.4%
Promise.allSettled (5 mixed) 13,111 ops/sec [13,030..13,239] → 17,413 ops/sec [17,046..17,963] 🟢 +32.8% 13,604 ops/sec [13,292..13,730] → 13,781 ops/sec [13,605..14,000] ~ overlap (+1.3%)
Promise.any (5 mixed) 15,542 ops/sec [15,197..15,672] → 20,922 ops/sec [20,222..21,045] 🟢 +34.6% 15,796 ops/sec [15,448..15,955] → 16,099 ops/sec [15,633..16,214] ~ overlap (+1.9%)
regexp.js — Interp: 🟢 11 · avg +41.3% · Bytecode: 🟢 10, 1 unch. · avg +10.7%
Benchmark Interpreted Δ Bytecode Δ
regex literal creation 69,061 ops/sec [68,369..70,289] → 96,799 ops/sec [95,428..98,114] 🟢 +40.2% 64,398 ops/sec [63,219..65,199] → 71,624 ops/sec [70,605..71,917] 🟢 +11.2%
new RegExp(pattern, flags) 59,206 ops/sec [58,870..59,888] → 84,298 ops/sec [82,274..84,837] 🟢 +42.4% 61,289 ops/sec [60,002..62,078] → 68,296 ops/sec [64,714..70,057] 🟢 +11.4%
RegExp(existingRegex) returns the same regex 248,108 ops/sec [244,214..249,448] → 346,652 ops/sec [340,477..360,637] 🟢 +39.7% 406,778 ops/sec [383,216..414,418] → 444,334 ops/sec [443,668..446,352] 🟢 +9.2%
test() on a global regex 63,681 ops/sec [62,259..65,499] → 89,207 ops/sec [78,715..91,125] 🟢 +40.1% 72,419 ops/sec [70,974..73,079] → 80,849 ops/sec [80,348..82,082] 🟢 +11.6%
exec() with capture groups 53,907 ops/sec [52,842..54,828] → 77,404 ops/sec [76,264..80,443] 🟢 +43.6% 60,672 ops/sec [59,716..61,295] → 68,849 ops/sec [66,457..70,713] 🟢 +13.5%
toString() 184,556 ops/sec [182,886..185,632] → 262,154 ops/sec [255,009..274,714] 🟢 +42.0% 240,115 ops/sec [236,576..243,680] → 265,957 ops/sec [264,531..266,409] 🟢 +10.8%
match() with global regex 19,145 ops/sec [18,788..19,540] → 26,976 ops/sec [26,384..27,145] 🟢 +40.9% 19,325 ops/sec [19,042..19,525] → 21,082 ops/sec [20,342..21,931] 🟢 +9.1%
matchAll() with capture groups 9,853 ops/sec [9,817..9,882] → 13,749 ops/sec [13,374..14,265] 🟢 +39.5% 11,740 ops/sec [11,672..11,811] → 12,897 ops/sec [12,742..13,589] 🟢 +9.8%
replace() with global regex 18,547 ops/sec [18,195..18,735] → 26,377 ops/sec [25,664..26,809] 🟢 +42.2% 18,544 ops/sec [17,943..18,813] → 20,187 ops/sec [19,998..20,275] 🟢 +8.9%
search() with regex 37,743 ops/sec [37,272..38,445] → 53,007 ops/sec [52,062..54,405] 🟢 +40.4% 36,453 ops/sec [36,033..36,773] → 40,623 ops/sec [40,380..40,676] 🟢 +11.4%
split() with regex separator 18,679 ops/sec [18,621..19,100] → 26,666 ops/sec [26,141..27,634] 🟢 +42.8% 18,850 ops/sec [18,666..19,267] → 20,864 ops/sec [18,312..21,003] ~ overlap (+10.7%)
strings.js — Interp: 🟢 19 · avg +35.6% · Bytecode: 🟢 14, 5 unch. · avg +6.3%
Benchmark Interpreted Δ Bytecode Δ
string concatenation 153,048 ops/sec [150,421..155,979] → 203,785 ops/sec [198,977..207,882] 🟢 +33.2% 945,230 ops/sec [939,030..952,812] → 974,422 ops/sec [957,692..991,011] 🟢 +3.1%
template literal 292,985 ops/sec [281,284..301,012] → 400,474 ops/sec [388,556..402,623] 🟢 +36.7% 637,721 ops/sec [622,954..643,829] → 646,022 ops/sec [630,212..684,789] ~ overlap (+1.3%)
string repeat 168,351 ops/sec [163,043..174,599] → 226,945 ops/sec [223,532..227,663] 🟢 +34.8% 210,664 ops/sec [210,127..211,224] → 211,034 ops/sec [208,087..213,173] ~ overlap (+0.2%)
split and join 28,976 ops/sec [27,951..29,671] → 39,277 ops/sec [38,822..39,680] 🟢 +35.5% 29,024 ops/sec [28,672..29,407] → 31,833 ops/sec [31,402..33,409] 🟢 +9.7%
indexOf and includes 51,984 ops/sec [50,630..55,588] → 73,083 ops/sec [72,064..74,264] 🟢 +40.6% 50,854 ops/sec [49,852..51,549] → 54,164 ops/sec [53,175..55,390] 🟢 +6.5%
toUpperCase and toLowerCase 84,545 ops/sec [83,074..85,965] → 116,463 ops/sec [113,435..116,836] 🟢 +37.8% 93,842 ops/sec [93,136..96,805] → 94,740 ops/sec [85,136..95,775] ~ overlap (+1.0%)
slice and substring 52,101 ops/sec [50,785..53,628] → 70,434 ops/sec [69,866..72,287] 🟢 +35.2% 54,915 ops/sec [54,674..55,882] → 58,574 ops/sec [50,773..60,700] ~ overlap (+6.7%)
trim operations 79,737 ops/sec [78,489..80,411] → 106,125 ops/sec [105,198..107,144] 🟢 +33.1% 82,596 ops/sec [81,435..85,074] → 85,905 ops/sec [84,285..87,012] ~ overlap (+4.0%)
replace and replaceAll 80,725 ops/sec [79,475..81,162] → 108,716 ops/sec [107,816..109,788] 🟢 +34.7% 74,356 ops/sec [71,492..76,475] → 81,570 ops/sec [80,083..83,942] 🟢 +9.7%
startsWith and endsWith 50,746 ops/sec [50,284..52,053] → 69,284 ops/sec [68,063..70,130] 🟢 +36.5% 44,919 ops/sec [44,396..45,921] → 48,258 ops/sec [46,096..48,677] 🟢 +7.4%
padStart and padEnd 75,351 ops/sec [73,082..76,046] → 101,094 ops/sec [99,343..101,935] 🟢 +34.2% 74,478 ops/sec [74,206..75,060] → 78,725 ops/sec [76,383..80,125] 🟢 +5.7%
identity tag, no substitutions 166,029 ops/sec [162,575..169,927] → 224,030 ops/sec [223,183..225,570] 🟢 +34.9% 554,083 ops/sec [534,770..565,987] → 625,277 ops/sec [618,434..626,170] 🟢 +12.8%
tag with 1 substitution 35,531 ops/sec [34,894..36,379] → 48,037 ops/sec [47,470..48,685] 🟢 +35.2% 54,316 ops/sec [52,737..54,409] → 56,792 ops/sec [56,190..57,338] 🟢 +4.6%
tag with 3 substitutions 19,209 ops/sec [18,656..19,434] → 25,983 ops/sec [25,431..26,052] 🟢 +35.3% 30,243 ops/sec [29,728..30,621] → 32,462 ops/sec [31,838..33,088] 🟢 +7.3%
tag with 6 substitutions 11,306 ops/sec [10,985..11,459] → 14,997 ops/sec [14,877..15,072] 🟢 +32.6% 17,774 ops/sec [17,459..18,424] → 19,435 ops/sec [19,193..19,737] 🟢 +9.3%
String.raw, no substitutions 226,495 ops/sec [223,294..231,973] → 306,963 ops/sec [300,341..312,075] 🟢 +35.5% 229,410 ops/sec [224,573..231,969] → 247,483 ops/sec [245,560..254,842] 🟢 +7.9%
String.raw, 2 substitutions 163,113 ops/sec [158,788..167,706] → 221,051 ops/sec [219,251..224,989] 🟢 +35.5% 148,265 ops/sec [147,476..148,987] → 163,170 ops/sec [158,681..166,417] 🟢 +10.1%
tag accessing .raw array 67,125 ops/sec [66,180..68,047] → 92,260 ops/sec [90,486..93,485] 🟢 +37.4% 91,012 ops/sec [89,396..91,704] → 94,621 ops/sec [92,962..95,815] 🟢 +4.0%
method as tag (this binding) 25,616 ops/sec [25,506..26,255] → 35,184 ops/sec [35,028..35,459] 🟢 +37.4% 40,028 ops/sec [39,673..40,148] → 43,690 ops/sec [43,087..44,047] 🟢 +9.1%
tsv.js — Interp: 🟢 9 · avg +45.4% · Bytecode: 🟢 8, 1 unch. · avg +10.5%
Benchmark Interpreted Δ Bytecode Δ
parse simple 3-column TSV 45,988 ops/sec [45,463..46,919] → 65,582 ops/sec [65,107..67,288] 🟢 +42.6% 50,645 ops/sec [49,899..50,840] → 51,746 ops/sec [50,639..52,473] ~ overlap (+2.2%)
parse 10-row TSV 12,637 ops/sec [12,496..12,728] → 18,252 ops/sec [17,951..18,352] 🟢 +44.4% 13,224 ops/sec [12,950..13,283] → 14,071 ops/sec [13,850..14,485] 🟢 +6.4%
parse 100-row TSV 1,932 ops/sec [1,858..2,042] → 2,795 ops/sec [2,771..2,922] 🟢 +44.7% 2,051 ops/sec [2,011..2,130] → 2,210 ops/sec [2,168..2,285] 🟢 +7.8%
parse TSV with backslash-escaped fields 9,117 ops/sec [9,081..9,141] → 13,101 ops/sec [12,613..13,215] 🟢 +43.7% 9,770 ops/sec [9,727..9,876] → 10,164 ops/sec [10,140..10,186] 🟢 +4.0%
parse without headers (array of arrays) 5,434 ops/sec [5,271..5,490] → 7,866 ops/sec [7,781..8,075] 🟢 +44.8% 6,031 ops/sec [5,969..6,080] → 6,184 ops/sec [6,105..6,236] 🟢 +2.5%
stringify array of objects 39,447 ops/sec [38,993..39,820] → 58,180 ops/sec [57,377..58,626] 🟢 +47.5% 40,349 ops/sec [38,801..40,498] → 46,165 ops/sec [45,660..46,805] 🟢 +14.4%
stringify array of arrays 11,253 ops/sec [11,095..11,563] → 16,726 ops/sec [16,618..16,920] 🟢 +48.6% 10,207 ops/sec [10,104..10,301] → 13,056 ops/sec [12,921..13,264] 🟢 +27.9%
stringify with values needing escaping 30,211 ops/sec [29,763..30,371] → 44,286 ops/sec [44,056..45,267] 🟢 +46.6% 30,516 ops/sec [30,215..31,081] → 35,421 ops/sec [35,080..35,574] 🟢 +16.1%
parse then stringify 6,979 ops/sec [6,886..7,037] → 10,152 ops/sec [10,109..10,189] 🟢 +45.5% 6,961 ops/sec [6,938..7,280] → 7,885 ops/sec [7,832..7,976] 🟢 +13.3%
typed-arrays.js — Interp: 🟢 20, 🔴 1, 1 unch. · avg +34.1% · Bytecode: 🟢 10, 🔴 6, 6 unch. · avg -11.0%
Benchmark Interpreted Δ Bytecode Δ
new Int32Array(0) 116,421 ops/sec [114,229..118,796] → 159,428 ops/sec [156,621..162,027] 🟢 +36.9% 139,668 ops/sec [138,492..142,823] → 147,103 ops/sec [146,342..156,877] 🟢 +5.3%
new Int32Array(100) 110,291 ops/sec [109,021..113,184] → 147,161 ops/sec [145,744..152,346] 🟢 +33.4% 133,856 ops/sec [130,175..136,197] → 139,917 ops/sec [137,743..145,468] 🟢 +4.5%
new Int32Array(1000) 86,245 ops/sec [85,427..86,637] → 108,536 ops/sec [106,816..109,492] 🟢 +25.8% 101,236 ops/sec [99,522..101,544] → 95,103 ops/sec [90,409..100,148] ~ overlap (-6.1%)
new Float64Array(100) 111,858 ops/sec [110,959..113,110] → 141,632 ops/sec [141,002..146,383] 🟢 +26.6% 128,243 ops/sec [127,591..129,826] → 135,206 ops/sec [129,569..137,001] ~ overlap (+5.4%)
Int32Array.from([...]) 5,004 ops/sec [4,921..5,088] → 6,314 ops/sec [6,200..6,455] 🟢 +26.2% 5,177 ops/sec [5,062..5,225] → 5,394 ops/sec [5,260..5,404] 🟢 +4.2%
Int32Array.of(1, 2, 3, 4, 5) 132,817 ops/sec [128,471..134,070] → 172,791 ops/sec [171,800..175,129] 🟢 +30.1% 149,262 ops/sec [148,430..150,648] → 164,050 ops/sec [162,842..165,026] 🟢 +9.9%
sequential write 100 elements 1,502 ops/sec [1,499..1,506] → 1,933 ops/sec [1,917..1,954] 🟢 +28.7% 6,804 ops/sec [6,678..6,856] → 7,129 ops/sec [6,909..7,219] 🟢 +4.8%
sequential read 100 elements 1,620 ops/sec [1,613..1,628] → 1,989 ops/sec [1,978..2,022] 🟢 +22.8% 6,736 ops/sec [6,703..6,773] → 6,902 ops/sec [6,864..6,907] 🟢 +2.5%
Float64Array write 100 elements 2,151 ops/sec [1,350..2,192] → 1,807 ops/sec [1,761..1,839] ~ overlap (-16.0%) 4,591 ops/sec [4,557..4,624] → 4,643 ops/sec [4,547..4,693] ~ overlap (+1.1%)
fill(42) 42,818 ops/sec [42,541..43,069] → 35,776 ops/sec [35,586..35,929] 🔴 -16.4% 28,872 ops/sec [27,309..29,573] → 29,335 ops/sec [29,039..29,516] ~ overlap (+1.6%)
slice() 107,821 ops/sec [105,669..109,894] → 144,976 ops/sec [144,498..145,696] 🟢 +34.5% 204,022 ops/sec [189,580..206,221] → 132,738 ops/sec [132,026..134,191] 🔴 -34.9%
map(x => x * 2) 2,573 ops/sec [2,558..2,583] → 3,519 ops/sec [3,459..3,613] 🟢 +36.8% 7,693 ops/sec [7,687..7,709] → 4,511 ops/sec [4,457..4,528] 🔴 -41.4%
filter(x => x > 50) 2,694 ops/sec [2,619..2,705] → 3,656 ops/sec [3,572..3,727] 🟢 +35.7% 8,468 ops/sec [8,335..8,537] → 4,944 ops/sec [4,763..5,132] 🔴 -41.6%
reduce (sum) 2,650 ops/sec [2,628..2,687] → 3,518 ops/sec [3,384..3,624] 🟢 +32.7% 7,180 ops/sec [7,033..7,243] → 4,178 ops/sec [4,135..4,240] 🔴 -41.8%
sort() 99,642 ops/sec [99,307..100,357] → 135,115 ops/sec [130,884..137,477] 🟢 +35.6% 193,826 ops/sec [193,034..194,239] → 122,324 ops/sec [121,776..122,789] 🔴 -36.9%
indexOf() 192,355 ops/sec [191,624..193,034] → 284,068 ops/sec [262,549..406,756] 🟢 +47.7% 412,271 ops/sec [410,601..415,005] → 243,112 ops/sec [241,111..243,824] 🔴 -41.0%
reverse() 152,699 ops/sec [151,607..153,826] → 316,961 ops/sec [316,280..318,364] 🟢 +107.6% 321,979 ops/sec [173,205..325,221] → 206,113 ops/sec [203,323..207,561] ~ overlap (-36.0%)
create view over existing buffer 135,969 ops/sec [133,269..148,100] → 285,543 ops/sec [281,948..287,805] 🟢 +110.0% 162,420 ops/sec [158,955..164,295] → 176,366 ops/sec [174,727..275,667] 🟢 +8.6%
subarray() 287,529 ops/sec [284,453..290,744] → 370,579 ops/sec [366,708..373,524] 🟢 +28.9% 212,982 ops/sec [212,607..217,597] → 225,127 ops/sec [221,521..229,364] 🟢 +5.7%
set() from array 343,605 ops/sec [335,325..347,519] → 485,917 ops/sec [483,428..486,693] 🟢 +41.4% 263,368 ops/sec [260,804..264,855] → 284,468 ops/sec [281,865..468,058] 🟢 +8.0%
for-of loop 3,641 ops/sec [3,630..3,643] → 4,462 ops/sec [4,435..4,490] 🟢 +22.6% 10,634 ops/sec [10,595..10,651] → 12,060 ops/sec [11,479..16,949] 🟢 +13.4%
spread into array 14,450 ops/sec [14,259..14,565] → 17,013 ops/sec [16,654..17,224] 🟢 +17.7% 63,363 ops/sec [36,852..64,477] → 39,752 ops/sec [39,549..58,958] ~ overlap (-37.3%)
uint8array-encoding.js — Interp: 🟢 11, 🔴 7 · avg +21.6% · Bytecode: 🟢 11, 🔴 2, 5 unch. · avg +7.2%
Benchmark Interpreted Δ Bytecode Δ
short (5 bytes) 255,208 ops/sec [248,200..260,244] → 373,779 ops/sec [362,840..404,570] 🟢 +46.5% 359,947 ops/sec [356,903..368,811] → 391,676 ops/sec [389,365..392,869] 🟢 +8.8%
medium (450 bytes) 140,855 ops/sec [140,548..142,206] → 210,832 ops/sec [204,649..214,713] 🟢 +49.7% 172,028 ops/sec [170,660..173,805] → 185,187 ops/sec [184,822..190,859] 🟢 +7.6%
large (4096 bytes) 31,766 ops/sec [30,974..31,908] → 45,119 ops/sec [44,010..45,858] 🟢 +42.0% 33,133 ops/sec [32,657..33,473] → 34,793 ops/sec [34,329..35,617] 🟢 +5.0%
base64url alphabet 102,524 ops/sec [102,051..102,850] → 148,038 ops/sec [146,960..153,577] 🟢 +44.4% 107,504 ops/sec [107,343..109,443] → 120,449 ops/sec [117,620..124,321] 🟢 +12.0%
omitPadding 154,895 ops/sec [153,466..155,854] → 226,208 ops/sec [219,448..231,281] 🟢 +46.0% 186,598 ops/sec [180,672..190,787] → 193,871 ops/sec [191,977..195,757] 🟢 +3.9%
short (8 chars) 139,957 ops/sec [137,921..140,688] → 194,422 ops/sec [191,638..198,243] 🟢 +38.9% 155,145 ops/sec [152,827..156,943] → 170,475 ops/sec [169,224..171,179] 🟢 +9.9%
medium (600 chars) 69,697 ops/sec [69,126..70,239] → 96,386 ops/sec [93,645..100,182] 🟢 +38.3% 71,239 ops/sec [70,747..71,744] → 75,799 ops/sec [75,456..76,894] 🟢 +6.4%
large (5464 chars) 13,887 ops/sec [13,832..14,071] → 18,561 ops/sec [18,007..19,499] 🟢 +33.7% 13,679 ops/sec [13,597..13,762] → 13,914 ops/sec [13,802..14,259] 🟢 +1.7%
short (5 bytes) 262,143 ops/sec [261,386..275,233] → 395,401 ops/sec [394,087..396,112] 🟢 +50.8% 429,560 ops/sec [427,174..429,605] → 477,971 ops/sec [469,027..486,119] 🟢 +11.3%
medium (450 bytes) 122,922 ops/sec [121,335..123,596] → 183,315 ops/sec [179,154..184,731] 🟢 +49.1% 165,194 ops/sec [160,633..165,712] → 169,121 ops/sec [168,206..173,065] 🟢 +2.4%
large (4096 bytes) 22,826 ops/sec [22,646..22,968] → 34,121 ops/sec [33,653..35,169] 🟢 +49.5% 26,939 ops/sec [26,668..27,268] → 26,831 ops/sec [26,709..27,011] ~ overlap (-0.4%)
short (10 chars) 243,972 ops/sec [234,015..245,743] → 214,890 ops/sec [205,973..224,683] 🔴 -11.9% 174,892 ops/sec [174,068..294,214] → 181,731 ops/sec [180,307..186,617] ~ overlap (+3.9%)
medium (900 chars) 171,769 ops/sec [171,296..172,561] → 143,858 ops/sec [141,355..151,805] 🔴 -16.2% 116,698 ops/sec [115,890..117,367] → 119,035 ops/sec [114,310..120,640] ~ overlap (+2.0%)
large (8192 chars) 49,364 ops/sec [48,254..49,517] → 39,168 ops/sec [38,116..40,478] 🔴 -20.7% 30,787 ops/sec [30,490..31,470] → 28,592 ops/sec [27,807..33,744] ~ overlap (-7.1%)
setFromBase64 (450 bytes) 112,049 ops/sec [110,928..112,357] → 95,371 ops/sec [94,020..100,794] 🔴 -14.9% 75,952 ops/sec [75,711..76,282] → 80,011 ops/sec [79,648..86,180] 🟢 +5.3%
setFromHex (450 bytes) 159,195 ops/sec [158,408..160,405] → 138,749 ops/sec [136,313..142,610] 🔴 -12.8% 111,184 ops/sec [106,995..194,555] → 186,160 ops/sec [181,425..187,632] ~ overlap (+67.4%)
toBase64 → fromBase64 (450 bytes) 82,589 ops/sec [82,067..82,888] → 72,860 ops/sec [71,574..74,725] 🔴 -11.8% 90,166 ops/sec [89,854..90,603] → 88,625 ops/sec [87,854..89,122] 🔴 -1.7%
toHex → fromHex (450 bytes) 105,365 ops/sec [105,045..105,629] → 93,665 ops/sec [92,903..94,318] 🔴 -11.1% 123,222 ops/sec [122,590..123,720] → 111,904 ops/sec [111,263..112,829] 🔴 -9.2%
weak-collections.js — Interp: 🟢 11, 🔴 3, 1 unch. · avg +69.0% · Bytecode: 🟢 9, 🔴 4, 2 unch. · avg +14.0%
Benchmark Interpreted Δ Bytecode Δ
constructor from 50 entries 11,936 ops/sec [11,417..19,132] → 17,487 ops/sec [16,907..17,840] ~ overlap (+46.5%) 12,224 ops/sec [12,008..12,382] → 13,975 ops/sec [13,654..14,253] 🟢 +14.3%
set 50 object keys 7,349 ops/sec [7,320..7,424] → 6,190 ops/sec [6,046..6,266] 🔴 -15.8% 5,982 ops/sec [5,934..6,102] → 6,485 ops/sec [6,447..6,599] 🟢 +8.4%
get lookups (50 entries) 95,128 ops/sec [94,772..95,347] → 82,156 ops/sec [81,701..82,714] 🔴 -13.6% 90,614 ops/sec [89,118..91,577] → 96,999 ops/sec [96,499..97,605] 🟢 +7.0%
has checks (50 entries) 125,403 ops/sec [124,497..126,846] → 108,156 ops/sec [106,569..110,116] 🔴 -13.8% 117,763 ops/sec [114,831..119,773] → 123,546 ops/sec [122,339..124,787] 🟢 +4.9%
delete entries 4,140 ops/sec [4,123..4,173] → 5,822 ops/sec [5,622..9,292] 🟢 +40.6% 5,917 ops/sec [5,894..5,941] → 6,146 ops/sec [6,124..10,244] 🟢 +3.9%
non-registered symbol keys 16,308 ops/sec [10,040..16,443] → 22,464 ops/sec [22,310..22,639] 🟢 +37.7% 14,081 ops/sec [13,308..14,108] → 24,336 ops/sec [23,913..24,413] 🟢 +72.8%
getOrInsert 6,657 ops/sec [6,597..6,749] → 9,322 ops/sec [9,125..9,383] 🟢 +40.0% 5,268 ops/sec [5,240..5,591] → 9,141 ops/sec [9,084..9,232] 🟢 +73.5%
getOrInsertComputed 2,063 ops/sec [2,039..3,283] → 4,461 ops/sec [4,393..4,509] 🟢 +116.3% 2,808 ops/sec [2,776..2,831] → 4,410 ops/sec [4,346..4,421] 🟢 +57.1%
forced gc live-key retention 4,302 ops/sec [4,240..4,354] → 9,362 ops/sec [9,337..9,406] 🟢 +117.6% 5,460 ops/sec [5,411..5,496] → 8,622 ops/sec [8,455..8,639] 🟢 +57.9%
constructor from 50 values 15,411 ops/sec [14,878..15,555] → 37,462 ops/sec [37,217..37,517] 🟢 +143.1% 29,910 ops/sec [28,922..30,153] → 28,888 ops/sec [25,184..29,343] ~ overlap (-3.4%)
add 50 object values 4,826 ops/sec [4,746..4,856] → 10,743 ops/sec [10,676..10,838] 🟢 +122.6% 11,940 ops/sec [11,852..11,982] → 10,774 ops/sec [6,695..11,711] 🔴 -9.8%
has checks (50 values) 77,751 ops/sec [77,361..79,310] → 166,895 ops/sec [164,798..172,432] 🟢 +114.7% 205,621 ops/sec [118,909..209,895] → 204,527 ops/sec [124,804..206,988] ~ overlap (-0.5%)
delete values 12,273 ops/sec [12,067..12,391] → 28,477 ops/sec [28,308..28,633] 🟢 +132.0% 25,532 ops/sec [24,662..25,810] → 16,520 ops/sec [16,129..16,928] 🔴 -35.3%
non-registered symbol values 10,501 ops/sec [10,376..10,776] → 23,997 ops/sec [23,553..24,107] 🟢 +128.5% 26,437 ops/sec [25,604..26,666] → 16,146 ops/sec [15,834..16,248] 🔴 -38.9%
forced gc pruning smoke 8,207 ops/sec [6,126..8,237] → 11,308 ops/sec [11,214..11,379] 🟢 +37.8% 11,581 ops/sec [11,471..11,634] → 11,331 ops/sec [11,069..11,386] 🔴 -2.2%

Deterministic profile diff

Deterministic profile diff: no significant changes.

Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Suite Timing

Test Runner (interpreted: 8,918 passed; bytecode: 8,918 passed)
Metric Interpreted Bytecode
Total 8918 8918
Passed 8918 ✅ 8918 ✅
Workers 4 4
Test Duration 2.01s 2.21s
Lex (cumulative) 253.3ms 173.7ms
Parse (cumulative) 270.7ms 286.9ms
Compile (cumulative) 636.6ms
Execute (cumulative) 1.97s 1.81s
Engine Total (cumulative) 2.50s 2.91s
Lex (avg/worker) 63.3ms 43.4ms
Parse (avg/worker) 67.7ms 71.7ms
Compile (avg/worker) 159.2ms
Execute (avg/worker) 492.9ms 453.3ms
Engine Total (avg/worker) 624.0ms 727.6ms

Memory

GC rows aggregate the main thread plus all worker thread-local GCs. Test runner worker shutdown frees thread-local heaps in bulk; that shutdown reclamation is not counted as GC collections or collected objects.

Metric Interpreted Bytecode
GC Live 215.58 MiB 210.15 MiB
GC Peak Live 215.59 MiB 210.15 MiB
GC Allocated During Run 219.39 MiB 213.94 MiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 1 1
GC Collected Objects 74 74
Heap Start Allocated 145.7 KiB 145.7 KiB
Heap End Allocated 1.37 MiB 1.37 MiB
Heap Delta Allocated 1.23 MiB 1.23 MiB
Heap Delta Free 469.1 KiB 469.1 KiB
Benchmarks (interpreted: 407; bytecode: 407)
Metric Interpreted Bytecode
Total 407 407
Workers 4 4
Duration 2.58min 2.37min

Memory

GC rows aggregate the main thread plus all worker thread-local GCs. Benchmark runner performs explicit between-file collections, so collection and collected-object counts can be much higher than the test runner.

Metric Interpreted Bytecode
GC Live 3.23 MiB 3.23 MiB
GC Peak Live 123.60 MiB 74.64 MiB
GC Allocated During Run 19.60 GiB 10.35 GiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 2,859 2,650
GC Collected Objects 369,549,645 248,677,039
Heap Start Allocated 1.13 MiB 1.13 MiB
Heap End Allocated 1.13 MiB 1.13 MiB
Heap Delta Allocated 128 B 128 B

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

test262 Conformance

Category Run Passed Δ Pass Failed Pass-rate Δ Rate
built-ins 23,448 14,338 +1 9,109 61.1% ±0pp
harness 116 71 ±0 45 61.2% ±0pp
intl402 3,324 167 ±0 3,157 5.0% ±0pp
language 23,635 12,566 ±0 11,069 53.2% ±0pp
staging 1,483 469 ±0 1,012 31.6% ±0pp
total 52,006 27,611 +1 24,392 53.1% ±0pp

Areas closest to 100%

Area Pass rate Δ vs main Passing
built-ins/WeakSet 98.8% ±0pp 84 / 85
built-ins/WeakMap 98.6% ±0pp 139 / 141
language/asi 97.1% ±0pp 99 / 102
Per-test deltas (+1 / -0)

Newly passing (1):

  • built-ins/Number/prototype/toExponential/undefined-fractiondigits.js

Steady-state failures are non-blocking; regressions vs the cached main baseline (lower total pass count, or any PASS → non-PASS transition) fail the conformance gate. Measured on ubuntu-latest x64, bytecode mode. Areas grouped by the first two test262 path components; minimum 25 attempted tests, areas already at 100% excluded. Δ vs main compares against the most recent cached main baseline.

…, try-finally

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
source/units/Goccia.Compiler.Statements.pas (2)

1956-2053: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

for await...of still skips iterator cleanup on abrupt exit.

This path never mirrors the sync CompileForOfStatement cleanup flow: there’s no pending iterator-close entry and no iterator-close emission on break/return/throw. So the new upvalue fix works, but async iterators with observable return() cleanup still leak that side effect when the loop exits abruptly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Compiler.Statements.pas` around lines 1956 - 2053,
CompileForAwaitOfStatement is missing the iterator-close handling on abrupt
exits; mirror the sync CompileForOfStatement flow by registering a pending
finally entry for iterator cleanup before the loop and emitting an OP_ITER_CLOSE
on abrupt exits. Specifically, in CompileForAwaitOfStatement add the same
GPendingFinally push/entry (so GBreakFinallyBase/GContinueFinallyBase include
the iterator close), ensure BreakJumps/ContinueJumps are routed through that
finally region, and emit an OP_ITER_CLOSE (via EmitInstruction with
EncodeABC(OP_ITER_CLOSE,...)) at the cleanup site (and/or in the finally
trampoline) so breaks/returns/throws call the async iterator’s return() before
leaving; use the existing ExitJump, LoopStart, BreakJumps, ContinueJumps,
GPendingFinally and EmitJumpInstruction/ PatchJumpTarget locations to integrate
this behavior.

2957-2982: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pop pending-finally entries before compiling them on break.

EmitPendingCleanup and CompileContinueStatement snapshot and remove pending entries before compiling cleanup, but CompileBreakStatement still compiles them in-place. That means a finally containing its own abrupt completion can observe the same pending entry again and emit the cleanup twice. try { break; } finally { return 1; } is the minimal reproducer.

Suggested shape
 procedure CompileBreakStatement(const ACtx: TGocciaCompilationContext);
 var
-  I: Integer;
+  I, Count, Base: Integer;
   Entry: TPendingFinallyEntry;
   Local: TGocciaCompilerLocal;
+  Entries: array of TPendingFinallyEntry;
 begin
   if not Assigned(GBreakJumps) then
     Exit;

   if Assigned(GPendingFinally) and (GPendingFinally.Count > GBreakFinallyBase) then
-    for I := GPendingFinally.Count - 1 downto GBreakFinallyBase do
+  begin
+    Count := GPendingFinally.Count;
+    Base := GBreakFinallyBase;
+    SetLength(Entries, Count - Base);
+    for I := Base to Count - 1 do
+      Entries[I - Base] := GPendingFinally[I];
+    for I := Count - 1 downto Base do
     begin
-      Entry := GPendingFinally[I];
+      if GPendingFinally.Count > I then
+        GPendingFinally.Delete(I);
+      Entry := Entries[I - Base];
       EmitPendingEntryCleanup(ACtx, Entry, True);
     end;
+    for I := 0 to Length(Entries) - 1 do
+      GPendingFinally.Insert(Base + I, Entries[I]);
+  end;

   for I := ACtx.Scope.LocalCount - 1 downto 0 do
   begin
     Local := ACtx.Scope.GetLocal(I);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Compiler.Statements.pas` around lines 2957 - 2982, In
CompileBreakStatement the loop over GPendingFinally must operate on a snapshot
and remove those entries first (like CompileContinueStatement/EmitPendingCleanup
do) to avoid double-emitting; change the code so that you copy GPendingFinally
entries from index GBreakFinallyBase..Count-1 into a local list/array,
reduce/pop GPendingFinally to GBreakFinallyBase immediately, then iterate the
snapshot and call EmitPendingEntryCleanup(ACtx, Entry, True) for each; keep the
rest of CompileBreakStatement (local cleanup and GBreakJumps.Add(...))
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@source/units/Goccia.Compiler.Statements.pas`:
- Around line 1956-2053: CompileForAwaitOfStatement is missing the
iterator-close handling on abrupt exits; mirror the sync CompileForOfStatement
flow by registering a pending finally entry for iterator cleanup before the loop
and emitting an OP_ITER_CLOSE on abrupt exits. Specifically, in
CompileForAwaitOfStatement add the same GPendingFinally push/entry (so
GBreakFinallyBase/GContinueFinallyBase include the iterator close), ensure
BreakJumps/ContinueJumps are routed through that finally region, and emit an
OP_ITER_CLOSE (via EmitInstruction with EncodeABC(OP_ITER_CLOSE,...)) at the
cleanup site (and/or in the finally trampoline) so breaks/returns/throws call
the async iterator’s return() before leaving; use the existing ExitJump,
LoopStart, BreakJumps, ContinueJumps, GPendingFinally and EmitJumpInstruction/
PatchJumpTarget locations to integrate this behavior.
- Around line 2957-2982: In CompileBreakStatement the loop over GPendingFinally
must operate on a snapshot and remove those entries first (like
CompileContinueStatement/EmitPendingCleanup do) to avoid double-emitting; change
the code so that you copy GPendingFinally entries from index
GBreakFinallyBase..Count-1 into a local list/array, reduce/pop GPendingFinally
to GBreakFinallyBase immediately, then iterate the snapshot and call
EmitPendingEntryCleanup(ACtx, Entry, True) for each; keep the rest of
CompileBreakStatement (local cleanup and GBreakJumps.Add(...)) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a9f17dc5-484d-42ea-8697-fbfda5f7e9fa

📥 Commits

Reviewing files that changed from the base of the PR and between 8939510 and 2426612.

📒 Files selected for processing (2)
  • source/units/Goccia.Compiler.Statements.pas
  • tests/language/for-of/for-of-break-closure.js

frostney and others added 2 commits May 8, 2026 08:57
… is not bypassed

The switch compiler already routes break jumps through EndScope/OP_CLOSE_UPVALUE
by patching breaks before EndScope. Setting GBreakScopeDepth after BeginScope
ensures CompileBreakStatement does not emit a redundant inline close for the
switch scope's own locals.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mirror CompileContinueStatement's pattern: snapshot entries above
GBreakFinallyBase, remove from GPendingFinally before compiling
cleanup, then restore. Prevents double handler cleanup when a
finally block contains a nested break.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney merged commit 2e1fe70 into main May 8, 2026
14 checks passed
@frostney frostney deleted the issue-537-break-close-upvalue branch May 8, 2026 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working internal Refactoring, CI, tooling, cleanup spec compliance Mismatch against official JavaScript/TypeScript specification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Engine: bytecode break skips per-iteration OP_CLOSE_UPVALUE in for-of and traditional for loops

1 participant