Skip to content

Add atob and btoa global functions#256

Merged
frostney merged 3 commits into
mainfrom
t3code/atob-btoa-spec
Apr 10, 2026
Merged

Add atob and btoa global functions#256
frostney merged 3 commits into
mainfrom
t3code/atob-btoa-spec

Conversation

@frostney
Copy link
Copy Markdown
Owner

Summary

  • Added global atob and btoa built-ins with WHATWG-style base64 behavior.
  • Implemented InvalidCharacterError DOMException support for invalid Latin-1 and base64 input.
  • Added end-to-end tests covering encoding, decoding, whitespace handling, padding, Latin-1 edge cases, and error paths.
  • Documented the new globals in docs/built-ins.md.

Testing

  • Not run: ./build.pas testrunner && ./build/TestRunner tests
  • Not run: targeted built-in tests for atob and btoa
  • Not run: native Pascal test suite

- Implement WHATWG base64 encoding and forgiving decode
- Add InvalidCharacterError DOMException handling
- Cover both globals with built-in tests and docs
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 10, 2026

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: 0b77a7e0-9205-41b3-8d31-53484310d05f

📥 Commits

Reviewing files that changed from the base of the PR and between f8d2824 and d0c295a.

📒 Files selected for processing (1)
  • units/Goccia.Builtins.Globals.pas
🚧 Files skipped from review as they are similar to previous changes (1)
  • units/Goccia.Builtins.Globals.pas

📝 Walkthrough

Walkthrough

Adds global functions btoa(data) and atob(data) with full Base64 encode/decode semantics: btoa enforces Latin‑1 input (throws InvalidCharacterError for code points > U+00FF); atob implements forgiving decoding (strips ASCII whitespace, tolerates missing padding, validates alphabet and length). Documentation, tests, benchmarks, and DOMException-style error helpers added.

Changes

Cohort / File(s) Summary
Documentation
docs/built-ins.md
Documented new globals btoa(data) and atob(data), their semantics, input validation rules, and specified error behavior.
Runtime globals (Pascal)
units/Goccia.Builtins.Globals.pas
Added BtoaCallback and AtobCallback implementations and exposed btoa/atob bindings. Implements UTF‑8 codepoint iteration, Latin‑1 validation for btoa, whitespace stripping and forgiving padding/alphabet validation for atob, and uses base64 unit for encode/decode. Review UTF‑8 scanning, byte↔string mappings, and padding/validation logic.
Error names & helpers (Pascal)
units/Goccia.Constants.ErrorNames.pas, units/Goccia.Values.ErrorHelper.pas
Added INVALID_CHARACTER_ERROR_NAME constant and ThrowInvalidCharacterError() helper producing a DOMException-like object with legacy code = 5 and optional prototype wiring.
Tests
tests/built-ins/atob.js, tests/built-ins/btoa.js
New Jest suites covering existence, encoding/decoding correctness (including Latin‑1 bytes), whitespace stripping, forgiving padding, error cases (InvalidCharacterError), TypeError on missing args, and round‑trip interoperability.
Benchmarks
benchmarks/base64.js
Added benchmark suites (btoa, atob, round-trip) measuring encode/decode and round-trip performance on short, long, and Latin‑1 inputs.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant JS as JavaScript caller
participant Globals as TGocciaGlobals (btoa/atob)
participant Base64 as base64 unit
participant Errors as Error helper / DOMException

JS->>Globals: call btoa(data) / atob(data)
Globals->>Globals: validate args; iterate UTF‑8 code points (btoa) / strip whitespace & sanitize (atob)
alt invalid input
    Globals->>Errors: ThrowInvalidCharacterError(...)
    Errors-->>JS: throw DOMException-like error (name: "InvalidCharacterError", code: 5)
else valid input
    Globals->>Base64: EncodeStringBase64 / DecodeStringBase64
    Base64-->>Globals: bytes / decoded bytes
    Globals->>JS: return resulting string

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes a summary of the changes with implementation details and linked documentation, but the testing section shows that critical tests were not actually run—the author explicitly notes tests were not executed. Run the required tests (./build.pas testrunner && ./build/TestRunner tests, targeted built-in tests, and native Pascal test suite) before merging to verify the implementation is correct.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add atob and btoa global functions' directly and clearly describes the primary change: implementing two new global functions for base64 encoding and decoding.
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.

@frostney frostney added the spec compliance Mismatch against official JavaScript/TypeScript specification label Apr 10, 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: 3

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

Inline comments:
In `@tests/built-ins/btoa.js`:
- Around line 51-56: The test "coerces non-string arguments to string" currently
passes only string literals; update the expectations to actually pass non-string
values to btoa (e.g., call btoa(123), btoa(true), btoa(null), btoa(undefined))
while keeping the same expected Base64 outputs so the test verifies ToString
coercion in the btoa implementation; change the four expect(...) calls to use
the non-string inputs (123, true, null, undefined) while still asserting "MTIz",
"dHJ1ZQ==", "bnVsbA==", and "dW5kZWZpbmVk" respectively.
- Around line 38-49: Update the "throws InvalidCharacterError for characters >
U+00FF" test to assert the exact DOMException contract instead of just that
something threw: invoke btoa for each case inside a try/catch and in the catch
assert that error.name === "InvalidCharacterError" and error.code === 5 (keep
the same inputs: String.fromCharCode(0x0100), "中", and "abc" +
String.fromCharCode(0x100)); leave the "throws TypeError when called with no
arguments" test unchanged.

In `@units/Goccia.Builtins.Globals.pas`:
- Around line 713-763: The code in AtobCallback builds Cleaned and ResultStr by
repeated string concatenation causing O(n^2) allocations; instead preallocate
and fill buffers by index: for Cleaned, SetLength to Length(Data), iterate and
write accepted chars into Cleaned[Pos] (track Pos) then SetLength(Cleaned, Pos);
for ResultStr, compute max output size (Length(Decoded) * 2),
SetLength(ResultStr, MaxLen), write bytes/UTF-8 bytes into ResultStr by index
(tracking OutPos) and finally SetLength(ResultStr, OutPos); keep using
DecodeStringBase64, IsBase64Char and ThrowInvalidCharacterError unchanged, just
replace the concatenations with indexed writes to avoid quadratic behavior.
🪄 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: cd7f1050-a935-4541-8258-7f1840a1946e

📥 Commits

Reviewing files that changed from the base of the PR and between c948550 and 7db6c3f.

📒 Files selected for processing (6)
  • docs/built-ins.md
  • tests/built-ins/atob.js
  • tests/built-ins/btoa.js
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Constants.ErrorNames.pas
  • units/Goccia.Values.ErrorHelper.pas

Comment thread tests/built-ins/btoa.js
Comment thread tests/built-ins/btoa.js
Comment thread units/Goccia.Builtins.Globals.pas
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 10, 2026

Benchmark Results

291 benchmarks

Interpreted: 🟢 6 improved · 🔴 242 regressed · 🆕 10 new · 33 unchanged · avg -6.1%
Bytecode: 🟢 89 improved · 🔴 48 regressed · 🆕 10 new · 144 unchanged · avg +0.9%

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

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

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 4212 4212
Tests Passed 4171 ✅ 4212 ✅
Tests Skipped 41 0
Tests Test Duration 299.8ms 277.4ms
Tests Lex 82.1ms 57.3ms
Tests Parse 100.7ms 108.4ms
Tests Compile 65.1ms
Tests Execute 315.2ms 308.3ms
Tests Engine Total 498.0ms 539.1ms
Benchmarks Total 291 291
Benchmarks Duration 7.93min 6.93min

Measured on ubuntu-latest x64.

- Speed up whitespace stripping and Latin-1 decoding in `atob`
- Strengthen `btoa` tests for invalid input and coercion
- Add base64 benchmarks for `atob`/`btoa` round trips
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.

🧹 Nitpick comments (1)
units/Goccia.Builtins.Globals.pas (1)

607-652: Malformed UTF-8 input could yield incorrect code points.

The helper doesn't validate that continuation bytes actually have the 10xxxxxx pattern, and when a sequence is truncated, it produces a partial result without signaling an error. For btoa's Latin-1 check this is likely benign (input comes from ToStringLiteral.Value which should be valid UTF-8), but malformed input could theoretically pass the > $FF check incorrectly.

Consider adding continuation-byte validation for robustness:

🛡️ Optional hardening
 else if (B and $E0) = $C0 then
 begin
   Result := (B and $1F) shl 6;
   Inc(AIndex);
-  if AIndex <= Length(AUTF8String) then
-    Result := Result or (Ord(AUTF8String[AIndex]) and $3F);
+  if (AIndex <= Length(AUTF8String)) and ((Ord(AUTF8String[AIndex]) and $C0) = $80) then
+    Result := Result or (Ord(AUTF8String[AIndex]) and $3F)
+  else
+    Result := $FFFD; // Replacement character signals invalid sequence
   Inc(AIndex);
 end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Builtins.Globals.pas` around lines 607 - 652, NextUTF8CodePoint
currently assumes continuation bytes are valid and may produce incorrect code
points for malformed or truncated UTF-8; update the function to validate each
continuation byte before using it by checking AIndex <= Length(AUTF8String) and
that (Ord(AUTF8String[AIndex]) and $C0) = $80 for each continuation, and if a
check fails return the Unicode replacement character (U+FFFD / $FFFD) and
advance by one (or otherwise skip the invalid byte) so malformed sequences do
not produce partial/incorrect code points.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@units/Goccia.Builtins.Globals.pas`:
- Around line 607-652: NextUTF8CodePoint currently assumes continuation bytes
are valid and may produce incorrect code points for malformed or truncated
UTF-8; update the function to validate each continuation byte before using it by
checking AIndex <= Length(AUTF8String) and that (Ord(AUTF8String[AIndex]) and
$C0) = $80 for each continuation, and if a check fails return the Unicode
replacement character (U+FFFD / $FFFD) and advance by one (or otherwise skip the
invalid byte) so malformed sequences do not produce partial/incorrect code
points.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbe920a1-e4aa-4b68-9745-c30565bdb430

📥 Commits

Reviewing files that changed from the base of the PR and between 7db6c3f and f8d2824.

📒 Files selected for processing (3)
  • benchmarks/base64.js
  • tests/built-ins/btoa.js
  • units/Goccia.Builtins.Globals.pas

- Make UTF-8 decoding return U+FFFD for truncated or invalid sequences
- Tighten continuation-byte validation in the globals builtin decoder
@frostney frostney merged commit 010f8ec into main Apr 10, 2026
1 check passed
@frostney frostney deleted the t3code/atob-btoa-spec branch April 10, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

spec compliance Mismatch against official JavaScript/TypeScript specification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant