Skip to content

Extract shared text semantics utilities#439

Merged
frostney merged 6 commits into
mainfrom
t3code/unicode-common-utility
Apr 28, 2026
Merged

Extract shared text semantics utilities#439
frostney merged 6 commits into
mainfrom
t3code/unicode-common-utility

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 28, 2026

Summary

  • Add TextSemantics as the shared home for UTF-8 retagging, newline normalization, ECMAScript whitespace, Unicode case mapping, replacement expansion, UTF-8 iteration, and well-formed string handling.
  • Move file-backed text data paths across CLI config, import maps, source maps, JSON/JSON5/JSONL, YAML, CSV/TSV, text assets, bytecode, and runtime string handling onto the shared semantics helpers.
  • Simplify Goccia.TextFiles back to file I/O only and remove pass-through wrappers so callers use TextSemantics directly.
  • Closes String.prototype: missing locale methods, incomplete trim Unicode, replaceAll patterns #377.

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

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
gocciascript-homepage Ignored Ignored Preview Apr 28, 2026 9:03pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 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: d8e8d90c-d95b-40ff-972d-039392b64de2

📥 Commits

Reviewing files that changed from the base of the PR and between fab0a7d and a850328.

📒 Files selected for processing (4)
  • source/shared/FileUtils.pas
  • source/units/Goccia.ModuleResolver.pas
  • source/units/Goccia.Modules.Configuration.pas
  • source/units/Goccia.Modules.Resolver.pas
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/units/Goccia.Modules.Resolver.pas

📝 Walkthrough

Walkthrough

Adds a new TextSemantics unit centralizing UTF‑8 byte‑accurate utilities, switches file readers to read raw UTF‑8 bytes then retag/normalize via TextSemantics, updates many parsing, string, iterator, module-resolution, and test paths to use these primitives, and trims Goccia.TextFiles API.

Changes

Cohort / File(s) Summary
Core UTF‑8 primitives & tests
source/shared/TextSemantics.pas, source/shared/TextSemantics.Test.pas
New public unit with UTF‑8 retagging, codepoint read/write, seq-length, well‑formedness, casing, ECMAScript whitespace trimming, replacement expansion, newline normalization, line splitting/joining; plus targeted unit tests.
TextFiles API reduction & callers
source/units/Goccia.TextFiles.pas, source/units/Goccia.TextFiles.Test.pas, source/shared/FileUtils.pas
Goccia.TextFiles reduced to only ReadUTF8FileText; removed earlier helpers; added UTF‑8-aware path utilities in FileUtils; tests updated to assert raw byte preservation.
App entrypoints using file input
source/app/GocciaBundler.dpr, source/app/GocciaScriptLoader.dpr, source/app/GocciaBenchmarkRunner.dpr, source/app/GocciaREPL.dpr, source/app/GocciaTestRunner.dpr
Replaced direct line readers with ReadUTF8FileText + CreateUTF8FileTextLines and added TextSemantics to uses lists.
Module resolution / import maps / config / source maps
source/units/Goccia.Modules.Resolver.pas, source/units/Goccia.Modules.Configuration.pas, source/units/Goccia.Modules.Configuration.Test.pas, source/shared/CLI.ConfigFile.pas, source/shared/CLI.ConfigFile.Test.pas, source/units/Goccia.SourceMap.Consumer.pas, source/units/Goccia.SourceMap.Test.pas
Switch to UTF‑8-aware path APIs (ExpandUTF8FileName, UTF8FileExists, etc.), read import maps/source maps as UTF‑8 bytes (UTF8String) and retag via RetagUTF8Text; tests add WriteRawFile helpers and assert non‑ASCII preservation.
Parsers and structured-data readers
source/shared/JSONParser.pas, source/units/Goccia.JSON.pas, source/units/Goccia.JSONL.pas, source/units/Goccia.YAML.pas, source/units/Goccia.Builtins.JSON5.pas, source/units/Goccia.Bytecode.Binary.pas
Removed local UTF‑8 helpers and manual encoding/decoding; delegate sequence-length, codepoint encode/decode, escape handling, and retagging to TextSemantics; replace direct string casts with RetagUTF8Text.
CSV / TSV / JSONL public APIs & tests
source/units/Goccia.CSV.pas, source/units/Goccia.TSV.pas, tests/built-ins/JSONL/parse.js, tests/language/modules/tabular-import.js
Added UTF8String overloads that retag raw bytes and delegate to existing parse impls; new tests for JSONL Uint8Array decoding and CSV/TSV import fixtures verifying UTF‑8 preservation.
String prototype, iterator, regex & related tests
source/units/Goccia.Values.StringObjectValue.pas, source/units/Goccia.Values.Iterator.Concrete.pas, source/units/Goccia.RegExp.Engine.pas, source/units/Goccia.Values.Primitives.pas, source/units/Goccia.Values.StringObjectValue.pas, tests/built-ins/String/prototype/*
Add toLocaleLowerCase/toLocaleUpperCase; enforce non‑nullish this; switch trim/case/replace/codePoint/advance behaviors to TextSemantics primitives; update iterator to advance by UTF‑8 sequences; extensive new/updated String tests.
Utilities, coverage, engine, shims, small units
source/units/Goccia.Utils.pas, source/units/Goccia.Coverage.Report.pas, source/units/Goccia.Engine.pas, source/units/Goccia.Error.Messages.pas, source/units/Goccia.Shims.pas, source/units/Goccia.Values.*, source/units/Goccia.ModuleResolver.pas, source/units/Goccia.Modules.*
Multiple units updated to use TextSemantics instead of Goccia.TextFiles; ToIntegerFromArgs changed to ToIntegerOrInfinity semantics; added resource string for String.prototype nullish error; module resolver switched to UTF‑8 path APIs.
Tests & harness adjustments
source/shared/CLI.ConfigFile.Test.pas, source/units/Goccia.Modules.Configuration.Test.pas, source/units/Goccia.SourceMap.Test.pas, many tests/built-ins/String/prototype/*, source/units/Goccia.TextFiles.Test.pas
New and revised tests to write/read raw UTF‑8 bytes, assert byte preservation, and cover Unicode behavior for case, trim, replace/replaceAll, charAt/charCodeAt/substring/codePointAt, iterator, CSV/TSV imports, and JSONL Uint8Array parsing.

Sequence Diagram(s)

sequenceDiagram
  participant FS as FileSystem (ReadUTF8FileText)
  participant TS as TextSemantics
  participant Parser as Parser/Loader
  participant Runtime as Runtime/Consumer

  FS->>TS: return raw UTF8 bytes (UTF8String)
  TS->>TS: RetagUTF8Text / NormalizeUTF8NewlinesToLF / CreateUTF8FileTextLines
  TS->>Parser: provide retagged UTF‑8 string or TStringList
  Parser->>Runtime: parsed AST / values with preserved UTF‑8 strings
  Runtime->>TS: call UnicodeLower/Upper / ExpandReplacementPattern / AdvanceUTF8StringIndex as needed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: extracting shared text semantics utilities into a common module.
Description check ✅ Passed The description follows the template with a clear summary, linked issue reference, and completed testing checklist (3 of 4 items checked).
Linked Issues check ✅ Passed The PR comprehensively addresses #377 by implementing toLocaleLowerCase/toLocaleUpperCase, complete Unicode whitespace handling in trim methods, replacement pattern expansion for replaceAll, UTF-8 iteration, well-formed UTF-8 handling, and improved argument coercion across string methods.
Out of Scope Changes check ✅ Passed All changes are directly aligned with extracting shared text semantics utilities and moving file-backed text handling to use them. No unrelated changes were introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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

@coderabbitai coderabbitai Bot added new feature New feature or request spec compliance Mismatch against official JavaScript/TypeScript specification internal Refactoring, CI, tooling, cleanup labels Apr 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

Benchmark Results

386 benchmarks

Interpreted: 🟢 46 improved · 🔴 96 regressed · 244 unchanged · avg -1.2%
Bytecode: 🟢 14 improved · 🔴 340 regressed · 32 unchanged · avg -8.6%

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

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

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 8089 8089
Tests Passed 8048 ✅ 8089 ✅
Tests Skipped 41 0
Tests Workers 4 4
Tests Test Duration 1.90s 1.70s
Tests Lex (cumulative) 190.1ms 131.5ms
Tests Parse (cumulative) 280.6ms 278.8ms
Tests Compile (cumulative) 184.0ms
Tests Execute (cumulative) 2.28s 1.47s
Tests Engine Total (cumulative) 2.75s 2.07s
Tests Lex (avg/worker) 47.5ms 32.9ms
Tests Parse (avg/worker) 70.1ms 69.7ms
Tests Compile (avg/worker) 46.0ms
Tests Execute (avg/worker) 569.8ms 368.7ms
Tests Engine Total (avg/worker) 687.5ms 517.2ms
Benchmarks Total 386 386
Benchmarks Workers 4 4
Benchmarks Duration 3.06min 2.88min

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

Caution

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

⚠️ Outside diff range comments (1)
source/units/Goccia.Values.StringObjectValue.pas (1)

1843-1864: ⚠️ Potential issue | 🟠 Major

codePointAt now rejects lone-surrogate strings.

This path only succeeds when the selected index starts a well-formed UTF-8 sequence. After this PR, strings can still contain lone surrogates/ill-formed UTF-8 bytes (source/shared/JSONParser.pas now routes \uXXXX through CodePointToUTF8, while source/shared/TextSemantics.pas rejects those bytes in TryReadUTF8CodePoint), so '\uD800'.codePointAt(0) will fall into the failure branch and return undefined instead of the surrogate code unit. It also makes any index that lands inside a multibyte character return undefined, which is inconsistent with the runtime's current byte-indexed string operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Values.StringObjectValue.pas` around lines 1843 - 1864,
The current code in codePointAt (using ExtractStringValue, ToIntegerFromArgs and
TryReadUTF8CodePoint) returns undefined when TryReadUTF8CodePoint fails (which
rejects lone surrogates or when the index falls inside a multibyte sequence);
change the failure path to preserve the runtime's previous byte-indexed behavior
by falling back to returning the raw byte/code-unit at the requested position
instead of undefined: after TryReadUTF8CodePoint fails, read the single byte at
StringValue[Index+1] and return that numeric value as the code point (using the
same Result/TGocciaUndefinedLiteralValue flow), so lone-surrogate inputs and
indices inside multibyte characters yield the original byte/code-unit value.
🧹 Nitpick comments (2)
tests/built-ins/String/prototype/toLocaleLowerCase.js (1)

12-20: Add nullish-receiver error assertions for completeness.

This suite should also verify String.prototype.toLocaleLowerCase.call(null/undefined) throws TypeError to lock in RequireObjectCoercible behavior.

Suggested additional test
 describe("String.prototype.toLocaleLowerCase", () => {
@@
   test("coerces non-string receivers", () => {
     expect(String.prototype.toLocaleLowerCase.call(true)).toBe("true");
   });
+
+  test("throws for nullish receivers", () => {
+    expect(() => String.prototype.toLocaleLowerCase.call(null)).toThrow(TypeError);
+    expect(() => String.prototype.toLocaleLowerCase.call(undefined)).toThrow(TypeError);
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/built-ins/String/prototype/toLocaleLowerCase.js` around lines 12 - 20,
Add assertions that calling String.prototype.toLocaleLowerCase with nullish
receivers throws a TypeError: update the "coerces non-string receivers"/coercion
tests in the String.prototype.toLocaleLowerCase suite to include expect(() =>
String.prototype.toLocaleLowerCase.call(null)).toThrow(TypeError) and expect(()
=> String.prototype.toLocaleLowerCase.call(undefined)).toThrow(TypeError) so the
test locks in RequireObjectCoercible behavior for
String.prototype.toLocaleLowerCase.
tests/built-ins/String/prototype/trimEnd.js (1)

16-21: Expand this Unicode whitespace vector to the full ECMAScript set.

Line 17 currently checks only part of the required set; missing members (like \u2001-\u200A, tab/CR/LF) could regress without failing this test.

Suggested test vector expansion
-  const whitespace = "\u00A0\u1680\u2000\u2028\u2029\u202F\u205F\u3000\uFEFF";
+  const whitespace =
+    "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680" +
+    "\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A" +
+    "\u2028\u2029\u202F\u205F\u3000\uFEFF";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/built-ins/String/prototype/trimEnd.js` around lines 16 - 21, The test
"String.prototype.trimEnd removes ECMAScript Unicode whitespace" currently uses
an incomplete whitespace vector; update the local variable whitespace in that
test to include the full ECMAScript whitespace set (e.g. U+0009 TAB, U+000A LF,
U+000B VT, U+000C FF, U+000D CR, U+0020 SPACE, U+00A0 NO-BREAK SPACE, U+1680
OGHAM SPACE MARK, U+2000..U+200A EN QUAD..HAIR SPACE, U+2028 LINE SEPARATOR,
U+2029 PARAGRAPH SEPARATOR, U+202F NARROW NO-BREAK, U+205F MEDIUM MATHEMATICAL
SPACE, U+3000 IDEOGRAPHIC SPACE, U+FEFF ZERO WIDTH NO-BREAK) so the test
variable whitespace covers every required codepoint and the assertion using
(whitespace + "hello" + whitespace).trimEnd() remains valid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/shared/TextSemantics.pas`:
- Around line 305-317: The loop in ToWellFormedUTF8 calls TryReadUTF8CodePoint
and on failure skips the entire probed ByteLength, which can discard valid
bytes; instead compute the actual invalid subpart length and advance by that. On
failure, set n := 1 and then while (n < ByteLength) and (Index + n <=
Length(AText)) and (AText[Index + n] is a UTF-8 continuation byte, i.e. in
$80..$BF) do Inc(n); ensure n is at least 1 and does not exceed remaining
length, append UTF8_REPLACEMENT_CHARACTER, then Inc(Index, n) (use the same
Buffer.Append/Index/ByteLength symbols already in the diff). This preserves any
subsequent valid start bytes rather than skipping the optimistic probe width.

In `@tests/built-ins/String/prototype/replaceAll.js`:
- Around line 64-75: Update the test "replaceAll calls function replacer for
empty string search values" to assert the exact offsets array contents instead
of only its length: after collecting offsets in the offsets array inside the
replacer callback, replace expect(offsets.length).toBe(3) with an assertion that
offsets equals the sequence [0, 1, 2] (e.g., expect(offsets).toEqual([0, 1,
2])), ensuring the exact values and order produced by 'ab'.replaceAll('', ...)
are validated.

---

Outside diff comments:
In `@source/units/Goccia.Values.StringObjectValue.pas`:
- Around line 1843-1864: The current code in codePointAt (using
ExtractStringValue, ToIntegerFromArgs and TryReadUTF8CodePoint) returns
undefined when TryReadUTF8CodePoint fails (which rejects lone surrogates or when
the index falls inside a multibyte sequence); change the failure path to
preserve the runtime's previous byte-indexed behavior by falling back to
returning the raw byte/code-unit at the requested position instead of undefined:
after TryReadUTF8CodePoint fails, read the single byte at StringValue[Index+1]
and return that numeric value as the code point (using the same
Result/TGocciaUndefinedLiteralValue flow), so lone-surrogate inputs and indices
inside multibyte characters yield the original byte/code-unit value.

---

Nitpick comments:
In `@tests/built-ins/String/prototype/toLocaleLowerCase.js`:
- Around line 12-20: Add assertions that calling
String.prototype.toLocaleLowerCase with nullish receivers throws a TypeError:
update the "coerces non-string receivers"/coercion tests in the
String.prototype.toLocaleLowerCase suite to include expect(() =>
String.prototype.toLocaleLowerCase.call(null)).toThrow(TypeError) and expect(()
=> String.prototype.toLocaleLowerCase.call(undefined)).toThrow(TypeError) so the
test locks in RequireObjectCoercible behavior for
String.prototype.toLocaleLowerCase.

In `@tests/built-ins/String/prototype/trimEnd.js`:
- Around line 16-21: The test "String.prototype.trimEnd removes ECMAScript
Unicode whitespace" currently uses an incomplete whitespace vector; update the
local variable whitespace in that test to include the full ECMAScript whitespace
set (e.g. U+0009 TAB, U+000A LF, U+000B VT, U+000C FF, U+000D CR, U+0020 SPACE,
U+00A0 NO-BREAK SPACE, U+1680 OGHAM SPACE MARK, U+2000..U+200A EN QUAD..HAIR
SPACE, U+2028 LINE SEPARATOR, U+2029 PARAGRAPH SEPARATOR, U+202F NARROW
NO-BREAK, U+205F MEDIUM MATHEMATICAL SPACE, U+3000 IDEOGRAPHIC SPACE, U+FEFF
ZERO WIDTH NO-BREAK) so the test variable whitespace covers every required
codepoint and the assertion using (whitespace + "hello" + whitespace).trimEnd()
remains valid.
🪄 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: f0a2c5c4-87e1-4f4d-b098-069b3c0f2675

📥 Commits

Reviewing files that changed from the base of the PR and between 79ceb33 and 90ffa3a.

⛔ Files ignored due to path filters (2)
  • tests/language/modules/helpers/unicode-table.csv is excluded by !**/*.csv
  • tests/language/modules/helpers/unicode-table.tsv is excluded by !**/*.tsv
📒 Files selected for processing (54)
  • docs/built-ins-data-formats.md
  • docs/built-ins.md
  • docs/testing.md
  • source/app/GocciaBenchmarkRunner.dpr
  • source/app/GocciaBundler.dpr
  • source/app/GocciaREPL.dpr
  • source/app/GocciaScriptLoader.dpr
  • source/app/GocciaTestRunner.dpr
  • source/shared/CLI.ConfigFile.Test.pas
  • source/shared/CLI.ConfigFile.pas
  • source/shared/JSONParser.pas
  • source/shared/TextSemantics.Test.pas
  • source/shared/TextSemantics.pas
  • source/units/Goccia.Builtins.JSON5.pas
  • source/units/Goccia.Bytecode.Binary.pas
  • source/units/Goccia.CSV.pas
  • source/units/Goccia.Compiler.Test.pas
  • source/units/Goccia.Coverage.Report.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Error.Messages.pas
  • source/units/Goccia.JSON.pas
  • source/units/Goccia.JSONL.pas
  • source/units/Goccia.JSX.Transformer.pas
  • source/units/Goccia.Lexer.pas
  • source/units/Goccia.Modules.Configuration.Test.pas
  • source/units/Goccia.Modules.ContentProvider.Test.pas
  • source/units/Goccia.Modules.ContentProvider.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.Modules.Resolver.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.RegExp.Engine.pas
  • source/units/Goccia.SourceMap.Consumer.pas
  • source/units/Goccia.SourceMap.Test.pas
  • source/units/Goccia.TSV.pas
  • source/units/Goccia.TextFiles.Test.pas
  • source/units/Goccia.TextFiles.pas
  • source/units/Goccia.Utils.pas
  • source/units/Goccia.Values.Primitives.pas
  • source/units/Goccia.Values.StringObjectValue.pas
  • source/units/Goccia.YAML.pas
  • tests/built-ins/JSONL/parse.js
  • tests/built-ins/String/prototype/charAt.js
  • tests/built-ins/String/prototype/charCodeAt.js
  • tests/built-ins/String/prototype/replace.js
  • tests/built-ins/String/prototype/replaceAll.js
  • tests/built-ins/String/prototype/substring.js
  • tests/built-ins/String/prototype/toLocaleLowerCase.js
  • tests/built-ins/String/prototype/toLocaleUpperCase.js
  • tests/built-ins/String/prototype/toLowerCase.js
  • tests/built-ins/String/prototype/toUpperCase.js
  • tests/built-ins/String/prototype/trim.js
  • tests/built-ins/String/prototype/trimEnd.js
  • tests/built-ins/String/prototype/trimStart.js
  • tests/language/modules/tabular-import.js

Comment thread source/shared/TextSemantics.pas Outdated
Comment thread tests/built-ins/String/prototype/replaceAll.js
@frostney frostney force-pushed the t3code/unicode-common-utility branch from 90ffa3a to 6ba93ef Compare April 28, 2026 16:45
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.

♻️ Duplicate comments (2)
tests/built-ins/String/prototype/replaceAll.js (1)

64-75: ⚠️ Potential issue | 🟡 Minor

Assert exact callback offsets, not just count.

Line 74 only checks the number of calls. That can pass even if offset values/order are wrong. Assert the exact sequence from 'ab'.replaceAll('', ...): [0, 1, 2].

Suggested fix
     expect(result).toBe('0a1b2');
-    expect(offsets.length).toBe(3);
+    expect(offsets).toEqual([0, 1, 2]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/built-ins/String/prototype/replaceAll.js` around lines 64 - 75, The
test "replaceAll calls function replacer for empty string search values"
currently only asserts offsets.length; change it to assert the exact sequence of
callback offsets by checking offsets equals [0,1,2] (i.e., after collecting
offsets in the offsets array, replace the expect(offsets.length).toBe(3) with an
equality assertion that offsets is exactly [0, 1, 2]) so the test verifies both
order and values from 'ab'.replaceAll('', ...).
source/shared/TextSemantics.pas (1)

305-317: ⚠️ Potential issue | 🟠 Major

Don't skip valid bytes after a malformed UTF-8 starter.

When TryReadUTF8CodePoint fails after setting ByteLength to 2/3/4, this branch still advances by that full probed width. That drops valid bytes that happen to follow the bad prefix, e.g. #$E2 + '(' + #$A1 gets collapsed into a single replacement instead of preserving the (. Advance only across the invalid subpart actually consumed by the malformed sequence, and add a regression for that shape.

💡 Suggested fix
 function ToWellFormedUTF8(const AText: string): string;
 var
   Buffer: TStringBuffer;
   ByteLength: Integer;
   CodePoint: Cardinal;
+  InvalidLength: Integer;
   Index: Integer;
 begin
   Buffer := TStringBuffer.Create(Length(AText));
   Index := 1;
   while Index <= Length(AText) do
@@
     end
     else
     begin
       Buffer.Append(UTF8_REPLACEMENT_CHARACTER);
-      if ByteLength < 1 then
-        ByteLength := 1;
-      if Index + ByteLength - 1 > Length(AText) then
-        ByteLength := Length(AText) - Index + 1;
-      Inc(Index, ByteLength);
+      if ByteLength < 1 then
+        ByteLength := 1;
+      InvalidLength := 1;
+      while (InvalidLength < ByteLength) and
+            (Index + InvalidLength <= Length(AText)) and
+            ((Ord(AText[Index + InvalidLength]) and $C0) = $80) do
+        Inc(InvalidLength);
+      Inc(Index, InvalidLength);
     end;
   end;
   Result := Buffer.ToString;
 end;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/shared/TextSemantics.pas` around lines 305 - 317, When
TryReadUTF8CodePoint fails in the decoding loop (the branch that appends
UTF8_REPLACEMENT_CHARACTER), do not unconditionally advance Index by the probed
ByteLength; instead compute the actual number of bytes consumed by the malformed
sequence by scanning from Index+1 up to Index+ByteLength-1 and only including
bytes that are valid UTF‑8 continuation bytes (0x80..0xBF), then advance Index
by 1 + that count (or at least 1 if none). Update the failing branch around
TryReadUTF8CodePoint/ByteLength/Index so
Buffer.Append(UTF8_REPLACEMENT_CHARACTER) is followed by this limited-scan
increment logic and add a regression test that verifies sequences like #$E2 +
'(' + #$A1 preserve '(' (e.g., expecting replacement + '(' + replacement).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@source/shared/TextSemantics.pas`:
- Around line 305-317: When TryReadUTF8CodePoint fails in the decoding loop (the
branch that appends UTF8_REPLACEMENT_CHARACTER), do not unconditionally advance
Index by the probed ByteLength; instead compute the actual number of bytes
consumed by the malformed sequence by scanning from Index+1 up to
Index+ByteLength-1 and only including bytes that are valid UTF‑8 continuation
bytes (0x80..0xBF), then advance Index by 1 + that count (or at least 1 if
none). Update the failing branch around TryReadUTF8CodePoint/ByteLength/Index so
Buffer.Append(UTF8_REPLACEMENT_CHARACTER) is followed by this limited-scan
increment logic and add a regression test that verifies sequences like #$E2 +
'(' + #$A1 preserve '(' (e.g., expecting replacement + '(' + replacement).

In `@tests/built-ins/String/prototype/replaceAll.js`:
- Around line 64-75: The test "replaceAll calls function replacer for empty
string search values" currently only asserts offsets.length; change it to assert
the exact sequence of callback offsets by checking offsets equals [0,1,2] (i.e.,
after collecting offsets in the offsets array, replace the
expect(offsets.length).toBe(3) with an equality assertion that offsets is
exactly [0, 1, 2]) so the test verifies both order and values from
'ab'.replaceAll('', ...).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c73d41bd-1296-4ac0-aca6-4474c7deeecb

📥 Commits

Reviewing files that changed from the base of the PR and between 90ffa3a and 6ba93ef.

⛔ Files ignored due to path filters (2)
  • tests/language/modules/helpers/unicode-table.csv is excluded by !**/*.csv
  • tests/language/modules/helpers/unicode-table.tsv is excluded by !**/*.tsv
📒 Files selected for processing (55)
  • docs/built-ins-data-formats.md
  • docs/built-ins.md
  • docs/testing.md
  • source/app/GocciaBenchmarkRunner.dpr
  • source/app/GocciaBundler.dpr
  • source/app/GocciaREPL.dpr
  • source/app/GocciaScriptLoader.dpr
  • source/app/GocciaTestRunner.dpr
  • source/shared/CLI.ConfigFile.Test.pas
  • source/shared/CLI.ConfigFile.pas
  • source/shared/JSONParser.pas
  • source/shared/TextSemantics.Test.pas
  • source/shared/TextSemantics.pas
  • source/units/Goccia.Builtins.JSON5.pas
  • source/units/Goccia.Bytecode.Binary.pas
  • source/units/Goccia.CSV.pas
  • source/units/Goccia.Compiler.Test.pas
  • source/units/Goccia.Coverage.Report.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Error.Messages.pas
  • source/units/Goccia.JSON.pas
  • source/units/Goccia.JSONL.pas
  • source/units/Goccia.JSX.Transformer.pas
  • source/units/Goccia.Lexer.pas
  • source/units/Goccia.Modules.Configuration.Test.pas
  • source/units/Goccia.Modules.ContentProvider.Test.pas
  • source/units/Goccia.Modules.ContentProvider.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.Modules.Resolver.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.RegExp.Engine.pas
  • source/units/Goccia.SourceMap.Consumer.pas
  • source/units/Goccia.SourceMap.Test.pas
  • source/units/Goccia.TSV.pas
  • source/units/Goccia.TextFiles.Test.pas
  • source/units/Goccia.TextFiles.pas
  • source/units/Goccia.Utils.pas
  • source/units/Goccia.Values.ObjectValue.Test.pas
  • source/units/Goccia.Values.Primitives.pas
  • source/units/Goccia.Values.StringObjectValue.pas
  • source/units/Goccia.YAML.pas
  • tests/built-ins/JSONL/parse.js
  • tests/built-ins/String/prototype/charAt.js
  • tests/built-ins/String/prototype/charCodeAt.js
  • tests/built-ins/String/prototype/replace.js
  • tests/built-ins/String/prototype/replaceAll.js
  • tests/built-ins/String/prototype/substring.js
  • tests/built-ins/String/prototype/toLocaleLowerCase.js
  • tests/built-ins/String/prototype/toLocaleUpperCase.js
  • tests/built-ins/String/prototype/toLowerCase.js
  • tests/built-ins/String/prototype/toUpperCase.js
  • tests/built-ins/String/prototype/trim.js
  • tests/built-ins/String/prototype/trimEnd.js
  • tests/built-ins/String/prototype/trimStart.js
  • tests/language/modules/tabular-import.js
✅ Files skipped from review due to trivial changes (15)
  • source/app/GocciaREPL.dpr
  • source/units/Goccia.Modules.ContentProvider.pas
  • source/units/Goccia.Modules.ContentProvider.Test.pas
  • tests/built-ins/String/prototype/toLowerCase.js
  • source/units/Goccia.Modules.Loader.pas
  • tests/built-ins/String/prototype/toLocaleUpperCase.js
  • source/units/Goccia.Values.Primitives.pas
  • source/units/Goccia.Compiler.Test.pas
  • tests/built-ins/String/prototype/toLocaleLowerCase.js
  • source/units/Goccia.JSX.Transformer.pas
  • source/units/Goccia.Builtins.JSON5.pas
  • source/units/Goccia.Lexer.pas
  • source/units/Goccia.Engine.pas
  • docs/built-ins.md
  • source/units/Goccia.Coverage.Report.pas
🚧 Files skipped from review as they are similar to previous changes (24)
  • source/units/Goccia.Error.Messages.pas
  • tests/built-ins/String/prototype/toUpperCase.js
  • tests/built-ins/String/prototype/substring.js
  • tests/built-ins/String/prototype/trimStart.js
  • tests/built-ins/JSONL/parse.js
  • tests/built-ins/String/prototype/trim.js
  • tests/built-ins/String/prototype/trimEnd.js
  • source/units/Goccia.JSON.pas
  • source/app/GocciaBenchmarkRunner.dpr
  • source/units/Goccia.Bytecode.Binary.pas
  • source/units/Goccia.Modules.Resolver.pas
  • source/units/Goccia.JSONL.pas
  • source/app/GocciaBundler.dpr
  • tests/built-ins/String/prototype/charCodeAt.js
  • docs/built-ins-data-formats.md
  • source/units/Goccia.RegExp.Engine.pas
  • tests/built-ins/String/prototype/replace.js
  • tests/language/modules/tabular-import.js
  • source/units/Goccia.Utils.pas
  • source/units/Goccia.TextFiles.Test.pas
  • source/units/Goccia.Modules.Configuration.Test.pas
  • source/units/Goccia.SourceMap.Test.pas
  • source/units/Goccia.TextFiles.pas
  • source/units/Goccia.Values.StringObjectValue.pas

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)
source/shared/TextSemantics.pas (1)

294-327: The ToWellFormedUTF8 fix addresses the prior concern but still has an edge case.

The updated logic at lines 314-323 now computes InvalidLength by counting continuation bytes, which is an improvement over the prior approach. However, the condition at line 321 (if Index + InvalidLength - 1 > Length(AText)) can never be true after the while loop guard at line 318 already ensures Index + InvalidLength <= Length(AText). This check is dead code.

More importantly, for a lead byte like $E2 (3-byte sequence) followed by a non-continuation byte (e.g., $E2 + 'A'), ByteLength is set to 3, but the while loop exits immediately when IsContinuationByte returns false for 'A'. This correctly advances by 1 and preserves 'A'. The test at line 76-77 (ToWellFormedUTF8(#$E2 + 'AB')UTF8_REPLACEMENT_CHARACTER + 'AB') confirms this works.

However, consider a truncated sequence at end of string: #$E2#$80 (incomplete 3-byte). The loop sets InvalidLength := 1, then increments to 2 (since #$80 is a continuation). It advances by 2, which is correct. The dead code at 321-322 can be safely removed.

🧹 Remove dead code
       while (InvalidLength < ByteLength) and
         (Index + InvalidLength <= Length(AText)) and
         IsContinuationByte(AText, Index + InvalidLength) do
         Inc(InvalidLength);
-      if Index + InvalidLength - 1 > Length(AText) then
-        InvalidLength := Length(AText) - Index + 1;
       Inc(Index, InvalidLength);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/shared/TextSemantics.pas` around lines 294 - 327, In ToWellFormedUTF8,
remove the dead conditional that checks "if Index + InvalidLength - 1 >
Length(AText) then InvalidLength := Length(AText) - Index + 1" because the
preceding while loop already enforces Index + InvalidLength <= Length(AText);
locate the block using the symbols ToWellFormedUTF8, InvalidLength, ByteLength
and Index and delete that if/then branch so the function simply advances Index
by InvalidLength after counting continuation bytes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@source/shared/TextSemantics.pas`:
- Around line 294-327: In ToWellFormedUTF8, remove the dead conditional that
checks "if Index + InvalidLength - 1 > Length(AText) then InvalidLength :=
Length(AText) - Index + 1" because the preceding while loop already enforces
Index + InvalidLength <= Length(AText); locate the block using the symbols
ToWellFormedUTF8, InvalidLength, ByteLength and Index and delete that if/then
branch so the function simply advances Index by InvalidLength after counting
continuation bytes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2af0d32b-b081-43f8-ac16-e1f15b7de563

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba93ef and b617794.

📒 Files selected for processing (7)
  • source/shared/TextSemantics.Test.pas
  • source/shared/TextSemantics.pas
  • source/units/Goccia.Values.StringObjectValue.pas
  • tests/built-ins/String/prototype/codePointAt.js
  • tests/built-ins/String/prototype/replaceAll.js
  • tests/built-ins/String/prototype/toLocaleLowerCase.js
  • tests/built-ins/String/prototype/trimEnd.js
✅ Files skipped from review due to trivial changes (1)
  • tests/built-ins/String/prototype/toLocaleLowerCase.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/built-ins/String/prototype/trimEnd.js
  • tests/built-ins/String/prototype/replaceAll.js

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
source/units/Goccia.Shims.pas (1)

73-74: Inconsistent indentation inside the embedded JavaScript.

The const b0 declaration at line 74 has 4-space indentation but should be at 6 spaces since it's inside the else block. This doesn't affect runtime behavior but harms readability when debugging the shim source.

📝 Suggested indentation fix
-        '    } else {'#10 +
-        '    const b0: number = str.charCodeAt(i);'#10 +
+        '    } else {'#10 +
+        '      const b0: number = str.charCodeAt(i);'#10 +
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Shims.pas` around lines 73 - 74, Adjust the embedded
JavaScript indentation so declarations inside the else block are aligned with
the block body: move the "const b0: number = str.charCodeAt(i);" line to use 6
spaces (same indentation level as other lines inside the else) rather than 4
spaces so it visually sits inside the "else" block in the embedded JS string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/units/Goccia.Shims.pas`:
- Around line 68-102: The btoa shim's UTF-8 decoding logic (the block that uses
skip, b0, cp, width and pushes decoded values into codePoints) is incorrect;
remove that whole multi-byte detection section (the code referencing skip, b0,
cp, width and the if/else chains) and replace it with a simple iteration that
collects UTF-16 code units by calling str.charCodeAt(i) for each index and
pushing into codePoints (keep the existing codePoints variable), then ensure
downstream logic that converts codePoints to bytes enforces the WHATWG rule that
each code point must be ≤ 0xFF before base64 encoding.

---

Nitpick comments:
In `@source/units/Goccia.Shims.pas`:
- Around line 73-74: Adjust the embedded JavaScript indentation so declarations
inside the else block are aligned with the block body: move the "const b0:
number = str.charCodeAt(i);" line to use 6 spaces (same indentation level as
other lines inside the else) rather than 4 spaces so it visually sits inside the
"else" block in the embedded JS string.
🪄 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: 950aaf83-94ed-42e7-99c3-85edd9af2c4d

📥 Commits

Reviewing files that changed from the base of the PR and between b617794 and 8987205.

📒 Files selected for processing (1)
  • source/units/Goccia.Shims.pas

Comment thread source/units/Goccia.Shims.pas Outdated
@frostney frostney force-pushed the t3code/unicode-common-utility branch from 8987205 to 17442dd Compare April 28, 2026 19:57
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)
source/units/Goccia.Values.Iterator.Concrete.pas (1)

249-272: Optional: extract shared “read current code point + advance” helper.

AdvanceNext and DirectNext now duplicate the same ByteLength/Copy/Inc block. A private helper would reduce drift risk in future iterator changes.

Also applies to: 274-299

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Values.Iterator.Concrete.pas` around lines 249 - 272,
AdvanceNext and DirectNext duplicate the logic that computes ByteLength,
extracts the UTF‑8 code point (Copy) and increments FIndex; create a private
helper method (e.g., ReadAndAdvanceCodePoint or GetNextCodePoint) on
TGocciaStringIteratorValue that encapsulates: obtain StrVal from FSource, check
FIndex vs Length(StrVal), compute TextSemantics.UTF8SequenceLengthAt, return the
extracted substring and advance FIndex (and set FDone when at end) so both
AdvanceNext and DirectNext call this helper and preserve existing return
semantics (CreateIteratorResult with the same values).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@source/units/Goccia.Values.Iterator.Concrete.pas`:
- Around line 249-272: AdvanceNext and DirectNext duplicate the logic that
computes ByteLength, extracts the UTF‑8 code point (Copy) and increments FIndex;
create a private helper method (e.g., ReadAndAdvanceCodePoint or
GetNextCodePoint) on TGocciaStringIteratorValue that encapsulates: obtain StrVal
from FSource, check FIndex vs Length(StrVal), compute
TextSemantics.UTF8SequenceLengthAt, return the extracted substring and advance
FIndex (and set FDone when at end) so both AdvanceNext and DirectNext call this
helper and preserve existing return semantics (CreateIteratorResult with the
same values).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16519d89-470e-4e81-9ea3-3f4ec96e7fe3

📥 Commits

Reviewing files that changed from the base of the PR and between 8987205 and 17442dd.

📒 Files selected for processing (3)
  • source/units/Goccia.Shims.pas
  • source/units/Goccia.Values.Iterator.Concrete.pas
  • tests/built-ins/String/prototype/symbol-iterator.js
✅ Files skipped from review due to trivial changes (1)
  • tests/built-ins/String/prototype/symbol-iterator.js

@frostney frostney merged commit 7d208a4 into main Apr 28, 2026
58 checks passed
@frostney frostney deleted the t3code/unicode-common-utility branch April 28, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Refactoring, CI, tooling, cleanup new feature New feature or request spec compliance Mismatch against official JavaScript/TypeScript specification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

String.prototype: missing locale methods, incomplete trim Unicode, replaceAll patterns

1 participant