Skip to content

Make String.prototype.matchAll return a lazy iterator#206

Merged
frostney merged 2 commits into
mainfrom
feature/lazy-matchall-iterator
Apr 7, 2026
Merged

Make String.prototype.matchAll return a lazy iterator#206
frostney merged 2 commits into
mainfrom
feature/lazy-matchall-iterator

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 7, 2026

Summary

  • Replace eager array-backed matchAll iterator with a lazy TGocciaRegExpMatchAllIteratorValue that computes one match per .next() call, aligning with ES2026 §22.2.6.9 spec behavior.
  • Both RegExp.prototype[Symbol.matchAll] and the String.prototype.matchAll fallback path now return the lazy iterator.
  • New unit Goccia.Values.Iterator.RegExp.pas follows the established lazy iterator pattern (same as TGocciaLazyMapIteratorValue etc.), with proper GC MarkReferences, ToStringTag, and support for both global and non-global modes.

Closes #175

Testing

  • Verified no regressions and confirmed the new feature or bugfix in end-to-end JavaScript/TypeScript tests
  • Updated documentation
  • Optional: Verified no regressions and confirmed the new feature or bugfix in native Pascal tests (if AST, scope, evaluator, or value types changed)
  • Optional: Verified no benchmark regressions or confirmed benchmark coverage for the change

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Clarified that String.prototype.matchAll() returns a lazy iterator computing matches on demand per ES2026 specification.
  • Tests

    • Added comprehensive test coverage for lazy iterator behavior, including partial iteration, completion checks, and compatibility with spread operators and for..of loops.

Replace the eager array-backed iterator with a lazy
TGocciaRegExpMatchAllIteratorValue that computes one match per .next()
call, matching the ES2026 spec behavior. The new iterator stores a
cloned RegExp and advances FSearchIndex on demand. Both
RegExp.prototype[Symbol.matchAll] and the String.prototype.matchAll
fallback path now return this lazy iterator.

Closes #175

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

coderabbitai Bot commented Apr 7, 2026

Warning

Rate limit exceeded

@frostney has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 10 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 5 minutes and 10 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c03deb7-2854-4427-884b-b68a1a36950f

📥 Commits

Reviewing files that changed from the base of the PR and between 75e8f56 and 8e678f7.

📒 Files selected for processing (3)
  • tests/built-ins/RegExp/prototype/symbol-matchAll.js
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Values.Iterator.RegExp.pas
📝 Walkthrough

Walkthrough

The PR converts String.prototype.matchAll() and RegExp.prototype[Symbol.matchAll]() from eager array-backed iterators to lazy iterators that compute matches on demand according to the ES2026 spec, with comprehensive test coverage and updated documentation.

Changes

Cohort / File(s) Summary
Documentation Updates
docs/built-ins.md, docs/language-restrictions.md
Updated documentation to reflect that matchAll() now returns a lazy iterator computing matches on demand instead of an eager array-backed iterator.
Test Coverage
tests/built-ins/RegExp/prototype/symbol-matchAll.js, tests/built-ins/String/prototype/matchAll.js
Added comprehensive test cases verifying lazy iterator behavior, partial consumption without forcing full iteration, iterator completion semantics, and correct match result contents.
Lazy Iterator Implementation
units/Goccia.Values.Iterator.RegExp.pas
New unit introducing TGocciaRegExpMatchAllIteratorValue class that implements lazy regex matching by computing matches on demand via AdvanceNext() and DirectNext() methods, with GC traversal support.
Control Flow Refactoring
units/Goccia.Builtins.GlobalRegExp.pas, units/Goccia.Values.StringObjectValue.pas
Refactored RegExp.prototype[Symbol.matchAll] and String.prototype.matchAll to delegate iteration to the new lazy iterator instead of eagerly materializing all matches into temporary arrays.

Sequence Diagram

sequenceDiagram
    participant Consumer as Iterator Consumer
    participant Iterator as RegExpMatchAllIterator
    participant Regex as RegExp Object
    participant Engine as Match Engine

    Consumer->>Iterator: Create iterator<br/>(regex, input, global)
    activate Iterator
    Iterator->>Regex: Clone regex
    note over Iterator: Store state:<br/>SearchIndex = 0<br/>Global flag

    loop For Each next() call
        Consumer->>Iterator: next()
        Iterator->>Engine: MatchRegExpObject<br/>(input, SearchIndex)
        activate Engine
        Engine-->>Iterator: Match found<br/>at [start, end]
        deactivate Engine
        Iterator->>Iterator: Update SearchIndex<br/>to end position
        Iterator-->>Consumer: {value: match, done: false}
    end

    Note over Iterator: No more matches<br/>or done condition met
    Iterator-->>Consumer: {value: undefined, done: true}
    deactivate Iterator
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Matches computed at your command,
No eager arrays taking up land!
Lazy iteration, smooth and neat,
Makes the spec-style flow complete! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Make String.prototype.matchAll return a lazy iterator' clearly and concisely describes the main change: converting from eager to lazy iteration behavior.
Description check ✅ Passed The description follows the template structure with a clear Summary section explaining the change, notes issue closure, and testing checkboxes with appropriate items marked complete.
Linked Issues check ✅ Passed Code changes fully implement the requirements from #175: lazy iterator computation per .next() call, coverage for lazy consumption patterns, documentation updates, and consistent behavior across execution paths.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing lazy matchAll iteration: new iterator unit, refactored matchAll implementations, updated tests, and documentation. No extraneous changes detected.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/lazy-matchall-iterator

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 15 improved · 🔴 133 regressed · 126 unchanged · avg -1.8%
Bytecode: 🟢 208 improved · 🔴 19 regressed · 47 unchanged · avg +6.0%

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

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 Apr 7, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 3740 3740
Tests Passed 3699 ✅ 3740 ✅
Tests Skipped 41 0
Tests Test Duration 204.8ms 183.7ms
Tests Lex 70.0ms 47.7ms
Tests Parse 89.4ms 91.5ms
Tests Compile 53.9ms
Tests Execute 219.3ms 210.7ms
Tests Engine Total 378.8ms 403.8ms
Benchmarks Total 274 274
Benchmarks Duration 7.57min 6.58min

Measured on ubuntu-latest x64.

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@units/Goccia.Builtins.GlobalRegExp.pas`:
- Around line 444-447: The code treats sticky (PROP_STICKY) as enabling repeated
matchAll iteration, but per spec only global (PROP_GLOBAL) should; change the
determination of IsGlobal used when constructing the iterator (see RegexClone,
GetRegExpBooleanProperty, PROP_GLOBAL, PROP_STICKY and
TGocciaRegExpMatchAllIteratorValue.Create) so it only checks PROP_GLOBAL (remove
or ignore PROP_STICKY here) and leave sticky handling to the regex matching
logic elsewhere.

In `@units/Goccia.Values.Iterator.RegExp.pas`:
- Around line 40-49: The constructor TGocciaRegExpMatchAllIteratorValue
currently resets FSearchIndex := 0 and thus discards the cloned regex's
preserved lastIndex; instead, when the iterator was created from a cloned RegExp
for global or sticky patterns (FGlobal or sticky flag on the cloned object),
initialize FSearchIndex from the cloned RegExp's lastIndex property (the value
preserved by CloneRegExpObject / the ARegExp passed into the constructor) rather
than unconditionally zero—leave the zero initialization only for
non-global/non-sticky regexes; update TGocciaRegExpMatchAllIteratorValue.Create
to read the cloned object's lastIndex and assign it to FSearchIndex when
appropriate.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 396e3dfe-0824-438f-82d5-c73eca83887c

📥 Commits

Reviewing files that changed from the base of the PR and between dd45cdf and 75e8f56.

📒 Files selected for processing (7)
  • docs/built-ins.md
  • docs/language-restrictions.md
  • tests/built-ins/RegExp/prototype/symbol-matchAll.js
  • tests/built-ins/String/prototype/matchAll.js
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Values.Iterator.RegExp.pas
  • units/Goccia.Values.StringObjectValue.pas
💤 Files with no reviewable changes (1)
  • docs/language-restrictions.md

Comment thread units/Goccia.Builtins.GlobalRegExp.pas
Comment thread units/Goccia.Values.Iterator.RegExp.pas
…onour cloned lastIndex

Per ES2026 §22.2.6.9, the internal `global` boolean in CreateRegExpStringIterator
is derived from flags containing "g" — sticky (y) affects match positioning but
does not trigger repeated iteration. Also initialise FSearchIndex from the
cloned regex's lastIndex so that /a/g with lastIndex=2 starts matching at
index 2 as the spec requires.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney merged commit 5704b36 into main Apr 7, 2026
9 checks passed
@frostney frostney deleted the feature/lazy-matchall-iterator branch April 7, 2026 22:45
@frostney frostney added enhancement new feature New feature or request and removed enhancement labels Apr 9, 2026
@coderabbitai coderabbitai Bot mentioned this pull request May 14, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make String.prototype.matchAll return a lazy iterator

1 participant