Skip to content

Centralize source pipeline parser policy#682

Merged
frostney merged 12 commits into
mainfrom
more-docs-changes
May 25, 2026
Merged

Centralize source pipeline parser policy#682
frostney merged 12 commits into
mainfrom
more-docs-changes

Conversation

@frostney
Copy link
Copy Markdown
Owner

Summary

  • Centralize lexer/parser/source-map/preprocessor handling behind TGocciaSourcePipeline, with class result objects that return warnings as data and narrow parse APIs for modules, expressions, dynamic Function, and parser probes.
  • Move Goccia-specific CLI option metadata into Goccia.CLI.Options; table-drive compatibility flags and resolve them into TGocciaEngine.Compatibility / source-pipeline options instead of per-flag convenience properties.
  • Rename ASI to the compatibility surface (--compat-asi / "compat-asi") with no legacy alias, infer module source type for .mjs, and update source-pipeline / CLI-host documentation and tests.

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

Commands run:

  • ./build.pas clean testrunner
  • ./build/GocciaTestRunner tests --no-progress
  • ./build/GocciaTestRunner tests/language/modules --no-progress
  • ./build/GocciaTestRunner tests/language/source-type --no-progress
  • ./build/GocciaTestRunner tests/built-ins/Function/constructor --no-progress
  • ./build/GocciaTestRunner tests/language/expressions/string --no-progress
  • ./build/GocciaTestRunner tests/language/classes/private-fields-in-template-literals.js --no-progress
  • ./build/GocciaTestRunner tests/language/expressions/loose-equality/loose-equality.js --no-progress
  • ./build.pas testrunner loader bundler benchmarkrunner && ./format.pas --check
  • ./build.pas tests
  • ./format.pas --check

@vercel
Copy link
Copy Markdown

vercel Bot commented May 24, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
gocciascript-homepage Ignored Ignored Preview May 25, 2026 7:14am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 090da13a-eecd-4699-bb3e-609d3ac0d3c5

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee8948 and 554f6e8.

📒 Files selected for processing (4)
  • docs/interpreter.md
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.SourcePipeline.pas

📝 Walkthrough

Walkthrough

Adds TGocciaSourcePipeline; centralizes compatibility flags and new T* CLI option primitives; migrates parsing, timing, warnings, and source-map handling across engine, loaders, bundler, REPL, TestRunner, and BenchmarkRunner; infers .mjs as module by default; updates docs, tests, agent skills, and adds a logo build script.

Changes

Combined Change Cohort

Layer / File(s) Summary
TGocciaSourcePipeline and parse helpers
source/units/Goccia.SourcePipeline.pas
New end-to-end pipeline: preprocessors, compatibility flags, source-type options, parse helpers, timing, generated-lines, warnings, and Parse/ParseModuleSource/ParseExpression/ParseFunction* APIs.
CLI option primitives & compatibility registry
source/shared/CLI.Options.pas, source/app/Goccia.CLI.Options.pas
Replaces legacy TGoccia* option types with T* variants and TOptionArray; adds TParseError; implements Goccia.CLI.Options with engine/coverage/profiler option groups and compatibility-flag helpers (ResolveCompatibilityFlags, TryApplyCompatibilityFlagArg).
Parser API & options bundling
source/units/Goccia.Parser.pas
Adds TGocciaParserOptions record, removes individual parser booleans, and adds ApplyOptions/Options/AtEnd; updates interpolation parsing to use bundled options.
Engine integration and Execute refactor
source/units/Goccia.Engine.pas
Aliases pipeline types/constants, removes public per-flag properties, adds PrintSourcePipelineWarnings, and rewrites Execute to parse via TGocciaSourcePipeline and activate pipeline options, handling timings, warnings, coverage, and last source-map ownership.
Module loader, shims, evaluator updates
source/units/Goccia.Modules.Loader.pas, source/units/Goccia.Shims.pas, source/units/Goccia.Evaluator.pas
Consolidates loader Preprocessors/Compatibility, parses modules/shims via pipeline ParseModuleSource, evaluates program nodes, and updates evaluator fallback to ParseExpression.
Applications wired to pipeline and new options
source/app/GocciaScriptLoader.dpr, source/app/GocciaScriptLoaderBare.dpr, source/app/GocciaBundler.dpr, source/app/GocciaREPL.dpr, source/app/GocciaTestRunner.dpr, source/app/GocciaBenchmarkRunner.dpr
Refactors apps to build TGocciaSourcePipelineOptions from engine properties, call pipeline Parse/ParseModuleSource, consume ProgramNode/SourceMap/Timing/Warnings, set engine Compatibility/Preprocessors, and update CLI validation/error types and option field types.
Option/config parser & config-apply
source/shared/CLI.Parser.pas, source/shared/CLI.ConfigFile.pas
Parses args using TOptionArray/TOptionBase, raises TParseError on unknown/missing values, and updates ApplyConfigEntries/ResolveFlagOption signatures to use new option types.
File-extension inference and helpers
source/units/Goccia.FileExtensions.pas
Adds IsModuleSourceExtension/IsModuleSourceFileName and uses .mjs to infer module entry source type; resolves source-type defaults in ResolveSourceTypeOption.
Tests, scripts, docs, agent skills, utilities
scripts/*, tests/language/*, docs/*, .agents/skills/*, skills-lock.json
Renames ASI config/flags to compat-asi/--compat-asi across tests and scripts; adds .mjs inference tests; updates docs for shared source pipeline, compatibility flags, and CONTEXT/ADR formats; adds grill-with-docs, handoff, improve-codebase-architecture skills; adds scripts/build-logo.ts.

Sequence Diagram

sequenceDiagram
  participant CLI_Host as CLI Host
  participant SourcePipeline as TGocciaSourcePipeline
  participant Compiler as Compiler/ModuleBuilder
  participant Runtime as Executor/VM
  CLI_Host->>SourcePipeline: Parse(source, options: Preprocessors, Compatibility, SourceType)
  SourcePipeline-->>CLI_Host: ProgramNode + Warnings + Lex/Parse times + SourceMap + GeneratedLines
  CLI_Host->>Compiler: Compile(ProgramNode, SourceType, Compatibility)
  Compiler-->>CLI_Host: Compiled module/program
  CLI_Host->>Runtime: Execute(compiled)
  Runtime-->>CLI_Host: Result + coverage + timings
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 24, 2026

Suite Timing

Test Runner (interpreted: 9,798 passed; bytecode: 9,798 passed)
Metric Interpreted Bytecode
Total 9798 9798
Passed 9798 ✅ 9798 ✅
Workers 4 4
Test Duration 3.41s 3.21s
Lex (cumulative) 451.2ms 436.3ms
Parse (cumulative) 312.3ms 307.8ms
Compile (cumulative) 674.6ms
Execute (cumulative) 3.78s 2.92s
Engine Total (cumulative) 4.55s 4.34s
Lex (avg/worker) 112.8ms 109.1ms
Parse (avg/worker) 78.1ms 77.0ms
Compile (avg/worker) 168.6ms
Execute (avg/worker) 945.6ms 729.7ms
Engine Total (avg/worker) 1.14s 1.08s

Memory

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

Metric Interpreted Bytecode
GC Live 280.98 MiB 275.22 MiB
GC Peak Live 280.99 MiB 275.23 MiB
GC Allocated During Run 285.47 MiB 279.72 MiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 1 1
GC Collected Objects 88 88
Heap Start Allocated 159.1 KiB 159.1 KiB
Heap End Allocated 1.53 MiB 1.53 MiB
Heap Delta Allocated 1.37 MiB 1.37 MiB
Heap Delta Free 448.9 KiB 448.9 KiB
Benchmarks (interpreted: 407; bytecode: 407)
Metric Interpreted Bytecode
Total 407 407
Workers 4 4
Duration 2.51min 2.46min

Memory

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

Metric Interpreted Bytecode
GC Live 3.93 MiB 3.93 MiB
GC Peak Live 132.57 MiB 95.75 MiB
GC Allocated During Run 18.94 GiB 9.69 GiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 2,831 2,657
GC Collected Objects 345,258,186 220,965,857
Heap Start Allocated 1.27 MiB 1.27 MiB
Heap End Allocated 1.27 MiB 1.27 MiB
Heap Delta Allocated 128 B 128 B

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 24, 2026

Benchmark Results

407 benchmarks

Interpreted: 🟢 397 improved · 🔴 4 regressed · 6 unchanged · avg +41.4%
Bytecode: 🟢 95 improved · 🔴 129 regressed · 183 unchanged · avg -1.1%

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

Deterministic profile diff

Deterministic profile diff: no significant changes.

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 24, 2026

test262 Conformance

Category Run Passed Δ Pass Failed Pass-rate Δ Rate
built-ins 23,449 16,067 +1 7,377 68.5% ±0pp
harness 116 72 ±0 44 62.1% ±0pp
intl402 3,324 935 ±0 2,389 28.1% ±0pp
language 23,635 14,433 ±0 9,202 61.1% ±0pp
staging 1,484 571 ±0 910 38.5% ±0pp
total 52,008 32,078 +1 19,922 61.7% ±0pp

Areas closest to 100%

Area Pass rate Δ vs main Passing
built-ins/WeakMap 99.3% ±0pp 140 / 141
built-ins/WeakSet 98.8% ±0pp 84 / 85
language/future-reserved-words 98.1% ±0pp 53 / 54
Per-test deltas (+1 / -0)

Newly passing (1):

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

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

@frostney frostney marked this pull request as ready for review May 24, 2026 22:10
@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation new feature New feature or request internal Refactoring, CI, tooling, cleanup labels May 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (1)
source/app/Goccia.CLI.Application.pas (1)

516-527: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep invalid per-file source-type values from bypassing the root config.

Line 516 currently falls straight back to the file-extension default when a per-file source-type is malformed, even if the root config already supplied a valid source-type. That skips the documented CLI > per-file > root config > extension default precedence and can silently flip a file back to script/.mjs defaults instead of the inherited root setting.

Suggested fix
   if FindConfigEntry(AFileConfig, 'source-type', ValueStr) then
   begin
     NormalizedValue := LowerCase(Trim(ValueStr));
     if NormalizedValue = 'module' then
       Exit(Goccia.Engine.stModule);
     if NormalizedValue = 'script' then
       Exit(Goccia.Engine.stScript);
+    if AOption.Present then
+    begin
+      WriteLn(StdErr, Format(
+        'Warning: invalid per-file config value for "source-type": %s ' +
+        '(valid: script, module). Falling back to root config value.',
+        [ValueStr]));
+      if AOption.Matches(Goccia.CLI.Options.stModule) then
+        Exit(Goccia.Engine.stModule);
+      Exit(Goccia.Engine.stScript);
+    end;
     WriteLn(StdErr, Format(
       'Warning: invalid per-file config value for "source-type": %s '
       + '(valid: script, module). Falling back to file extension default.',
       [ValueStr]));
     Exit(DefaultSourceTypeForFileName(AFileName));
   end;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/app/Goccia.CLI.Application.pas` around lines 516 - 527, The per-file
branch treats an invalid ValueStr as a signal to immediately fall back to
DefaultSourceTypeForFileName(AFileName), which bypasses any valid root config;
change the logic so that when NormalizedValue is not "module" or "script" you
attempt to read the root-level 'source-type' (using FindConfigEntry on the root
config structure) and if that root value is valid return the corresponding
Goccia.Engine.stModule/stScript, otherwise log the warning and then return
DefaultSourceTypeForFileName(AFileName); use the existing symbols ValueStr,
NormalizedValue, FindConfigEntry, and DefaultSourceTypeForFileName(AFileName) to
locate and implement this check.
🧹 Nitpick comments (1)
scripts/build-logo.ts (1)

74-74: 💤 Low value

Consider extracting the ImageMagick floodfill command for readability.

The background-removal command on line 74 is quite long with multiple ImageMagick operations. While correct, it could be more maintainable by extracting parameters into named constants or breaking into logical steps with comments.

♻️ Example refactor for readability
+// Remove solid background: add temp border, flood-fill from corner, remove border
+const fillCmd = "color 0,0 floodfill";
 if (await sourceHasTransparency(source)) {
   await $`${magick} ${source} -resize ${traceSize} ${cutout}`;
 } else {
-  await $`${magick} ${source} -resize ${traceSize} -alpha set -bordercolor none -border 1x1 -fill none -fuzz ${opaqueBackgroundFuzz} -draw ${"color 0,0 floodfill"} -shave 1x1 ${cutout}`;
+  await $`${magick} ${source} 
+    -resize ${traceSize} 
+    -alpha set 
+    -bordercolor none -border 1x1 
+    -fill none -fuzz ${opaqueBackgroundFuzz} 
+    -draw ${fillCmd} 
+    -shave 1x1 
+    ${cutout}`;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build-logo.ts` at line 74, Extract the long ImageMagick invocation
into a named variable or helper to improve readability: build a descriptive
constant or function (e.g., buildFloodfillArgs or floodfillCommand) that
assembles the sequence of parameters currently in the template literal
(`${magick} ${source} -resize ${traceSize} -alpha set -bordercolor none -border
1x1 -fill none -fuzz ${opaqueBackgroundFuzz} -draw ${"color 0,0 floodfill"}
-shave 1x1 ${cutout}`), then call await $`${magick} ${floodfillArgs}` (or pass
the args array) from the place where the command is run; keep the same variables
(magick, source, traceSize, opaqueBackgroundFuzz, cutout) and add brief comments
for each logical step (resize, alpha/border setup, fuzz/draw floodfill, shave)
to document intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Engine.pas`:
- Around line 1266-1305: The module-execution path assigns FLastTiming.Result
from FExecutor.EvaluateModuleBody and then calls WaitForRuntimeIdle without
protecting that value; temp-root the EvaluateModuleBody return before
WaitForRuntimeIdle (as RunModule/RunModuleInScope do) by storing the result in a
local rooted variable (or using the interpreter's temp-root API) e.g. capture
the value from EvaluateModuleBody into a temp-rooted local (TempResult), call
WaitForRuntimeIdle, then set FLastTiming.Result from TempResult and continue to
set timing fields.
- Around line 1557-1580: BodyParseResult (returned by
TGocciaSourcePipeline.ParseFunctionBodyWrapper) is an owned object that is never
freed, leaking on every Function(...) call including error paths; ensure
BodyParseResult is freed on all exit paths by taking ownership and releasing it
in a try..finally (or equivalent) around the logic that uses it (the validity
check, ThrowSyntaxError call, the call to ParseDynamicFunctionWrapper, execution
via FExecutor.ExecuteDynamicFunction and the final ProgramNode.Free), i.e., free
BodyParseResult in a finally block after you've finished using it so both
success and error paths release the object.

In `@source/units/Goccia.Evaluator.pas`:
- Around line 6367-6378: The ParseExpression calls currently pass the hard-coded
source label 'template-expression', losing original source location; update both
branches in EvaluateTemplateExpression so TGocciaSourcePipeline.ParseExpression
receives a source identifier built from the current file path and the caller's
ALine/AColumn (e.g. combine AContext's file/path property with ALine and
AColumn) instead of the literal 'template-expression', and keep the existing use
of DeclaredPrivateNames and PipelineOptions unchanged so parsed
nodes/diagnostics remain anchored to the original ${...} location.

In `@source/units/Goccia.SourcePipeline.pas`:
- Around line 373-399: The parser helpers TGocciaParser.Expression and
TGocciaParser.ParseExpressionWithPrivateNames only parse a single expression and
do not enforce EOF, so update TGocciaSourcePipeline.ParseExpression to reject
trailing tokens: after calling Parser.Expression or
Parser.ParseExpressionWithPrivateNames, verify the parser/token stream is at EOF
(or the next token kind is EOF) and if not, free Result and return nil (or
raise/handle as existing error flow) so inputs like "foo() bar" or "a, b; c" are
rejected; use the parser's current token/position API (or inspect
Tokens/Parser.Token.Kind) to detect non-EOF trailing tokens and handle
accordingly.
- Around line 462-479: ParseDynamicFunctionWrapper currently constructs
TGocciaLexer/TGocciaParser directly and ignores AOptions.Preprocessors; modify
it so the generated Source is passed through AOptions.Preprocessors (or the same
preprocessing step used by Parse/ParseModuleSource) before creating
TGocciaLexer. Specifically, after building Source in
ParseDynamicFunctionWrapper, run each preprocessor in AOptions.Preprocessors
(preserving filename/context) to produce the final source string, then create
TGocciaLexer with that processed source and continue creating
TGocciaParser/ConfigureParser as before so JSX and other preprocessing options
are honored.
- Around line 149-151: The NonStrictModeEnabled flag is being forced on for
modules; change the logic in the assignment for Result.NonStrictModeEnabled so
it is only true when cfNonStrictMode is present in AOptions.Compatibility
(remove the OR check against AOptions.SourceType = stModule). Update the line
that sets Result.NonStrictModeEnabled to rely solely on (cfNonStrictMode in
AOptions.Compatibility) so module sources are not parsed in non-strict mode.

---

Outside diff comments:
In `@source/app/Goccia.CLI.Application.pas`:
- Around line 516-527: The per-file branch treats an invalid ValueStr as a
signal to immediately fall back to DefaultSourceTypeForFileName(AFileName),
which bypasses any valid root config; change the logic so that when
NormalizedValue is not "module" or "script" you attempt to read the root-level
'source-type' (using FindConfigEntry on the root config structure) and if that
root value is valid return the corresponding Goccia.Engine.stModule/stScript,
otherwise log the warning and then return
DefaultSourceTypeForFileName(AFileName); use the existing symbols ValueStr,
NormalizedValue, FindConfigEntry, and DefaultSourceTypeForFileName(AFileName) to
locate and implement this check.

---

Nitpick comments:
In `@scripts/build-logo.ts`:
- Line 74: Extract the long ImageMagick invocation into a named variable or
helper to improve readability: build a descriptive constant or function (e.g.,
buildFloodfillArgs or floodfillCommand) that assembles the sequence of
parameters currently in the template literal (`${magick} ${source} -resize
${traceSize} -alpha set -bordercolor none -border 1x1 -fill none -fuzz
${opaqueBackgroundFuzz} -draw ${"color 0,0 floodfill"} -shave 1x1 ${cutout}`),
then call await $`${magick} ${floodfillArgs}` (or pass the args array) from the
place where the command is run; keep the same variables (magick, source,
traceSize, opaqueBackgroundFuzz, cutout) and add brief comments for each logical
step (resize, alpha/border setup, fuzz/draw floodfill, shave) to document
intent.
🪄 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: 7cbb57a1-52cb-463c-b998-966f730ec850

📥 Commits

Reviewing files that changed from the base of the PR and between add34b7 and cb9a750.

⛔ Files ignored due to path filters (3)
  • logo.png is excluded by !**/*.png
  • logo.svg is excluded by !**/*.svg
  • website/public/logo.png is excluded by !**/*.png
📒 Files selected for processing (58)
  • .agents/skills/grill-with-docs/ADR-FORMAT.md
  • .agents/skills/grill-with-docs/CONTEXT-FORMAT.md
  • .agents/skills/grill-with-docs/SKILL.md
  • .agents/skills/handoff/SKILL.md
  • .agents/skills/improve-codebase-architecture/DEEPENING.md
  • .agents/skills/improve-codebase-architecture/HTML-REPORT.md
  • .agents/skills/improve-codebase-architecture/INTERFACE-DESIGN.md
  • .agents/skills/improve-codebase-architecture/LANGUAGE.md
  • .agents/skills/improve-codebase-architecture/SKILL.md
  • AGENTS.md
  • CONTEXT.md
  • README.md
  • docs/architecture.md
  • docs/build-system.md
  • docs/built-ins.md
  • docs/bytecode-vm.md
  • docs/decision-log.md
  • docs/embedding.md
  • docs/interpreter.md
  • docs/language-tables.md
  • docs/language.md
  • docs/spikes/lexer-performance-simplification.md
  • docs/spikes/parser-hot-dispatch-performance.md
  • docs/testing.md
  • scripts/build-logo.ts
  • scripts/fetch-e2e.sh
  • scripts/run_test262_suite.ts
  • scripts/test-cli-apps.ts
  • scripts/test-cli-config.ts
  • scripts/test-cli-lexer.ts
  • scripts/test-cli-parser.ts
  • scripts/test-cli.ts
  • skills-lock.json
  • source/app/Goccia.CLI.Application.pas
  • source/app/Goccia.CLI.EngineSetup.pas
  • source/app/Goccia.CLI.Help.pas
  • source/app/Goccia.CLI.Options.pas
  • source/app/GocciaBenchmarkRunner.dpr
  • source/app/GocciaBundler.dpr
  • source/app/GocciaREPL.dpr
  • source/app/GocciaScriptLoader.dpr
  • source/app/GocciaScriptLoaderBare.dpr
  • source/app/GocciaTestRunner.dpr
  • source/shared/CLI.ConfigFile.Test.pas
  • source/shared/CLI.ConfigFile.pas
  • source/shared/CLI.Options.pas
  • source/shared/CLI.Parser.Test.pas
  • source/shared/CLI.Parser.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.FileExtensions.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.Shims.pas
  • source/units/Goccia.SourcePipeline.pas
  • tests/language/asi/goccia.json
  • tests/language/modules/mjs-entry-source-type.mjs
  • tests/language/with/asi/goccia.json

Comment thread source/shared/CLI.Parser.pas
Comment thread source/units/Goccia.Engine.pas
Comment thread source/units/Goccia.Engine.pas
Comment thread source/units/Goccia.Evaluator.pas Outdated
Comment thread source/units/Goccia.SourcePipeline.pas Outdated
Comment thread source/units/Goccia.SourcePipeline.pas
Comment thread source/units/Goccia.SourcePipeline.pas Outdated
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.SourcePipeline.pas (1)

394-425: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor preprocessors in ParseExpression.

This helper still lexes AExpressionText directly, so JSX-enabled callers will fail here even though Parse and ParseDynamicFunctionWrapper now go through ApplyPreprocessors.

Suggested fix
 class function TGocciaSourcePipeline.ParseExpression(const AExpressionText,
   AFileName: string; const AOptions: TGocciaSourcePipelineOptions;
   const ADeclaredPrivateNames: TStrings): TGocciaExpression;
 var
   Lexer: TGocciaLexer;
   Parser: TGocciaParser;
   SourceLines: TStringList;
   Tokens: TObjectList<TGocciaToken>;
+  ProcessedExpressionText: string;
+  PreprocessorSourceMap: TGocciaSourceMap;
 begin
   Result := nil;
   if AExpressionText = '' then
     Exit;
 
-  SourceLines := CreateECMAScriptSourceLines(AExpressionText);
+  ProcessedExpressionText := ApplyPreprocessors(AExpressionText, AOptions,
+    PreprocessorSourceMap);
+  SourceLines := CreateECMAScriptSourceLines(ProcessedExpressionText);
   try
-    Lexer := TGocciaLexer.Create(AExpressionText, AFileName);
+    Lexer := TGocciaLexer.Create(ProcessedExpressionText, AFileName);
     try
       Tokens := Lexer.ScanTokens;
       if not Assigned(Tokens) then
         Exit;
       Parser := TGocciaParser.Create(Tokens, AFileName, SourceLines);
@@
     finally
       Lexer.Free;
     end;
   finally
     SourceLines.Free;
+    PreprocessorSourceMap.Free;
   end;
 end;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.SourcePipeline.pas` around lines 394 - 425,
ParseExpression currently lexes AExpressionText directly (creating SourceLines
and TGocciaLexer with the raw text) so preprocessors (e.g., JSX handling) are
skipped; update TGocciaSourcePipeline.ParseExpression to run the expression text
through ApplyPreprocessors (using the provided AOptions) before calling
CreateECMAScriptSourceLines and TGocciaLexer.Create, then proceed to
ConfigureParser and parsing as before so Parser.ParseExpressionWithPrivateNames
/ Parser.Expression operate on the preprocessed text.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Engine.pas`:
- Around line 1240-1241: Initialize the local TGocciaValue variable ModuleResult
to nil before calling FExecutor.EvaluateModuleBody so that the finally block
cannot pass an uninitialized pointer to GC.RemoveTempRoot; additionally, in the
finally ensure you only call GC.RemoveTempRoot(ModuleResult) when
Assigned(ModuleResult) (or leave that guard in place if already present) to
avoid removing an unrelated temp root; relevant symbols: ModuleResult,
FExecutor.EvaluateModuleBody, GC.RemoveTempRoot,
TGarbageCollector.RemoveTempRoot.

In `@source/units/Goccia.Evaluator.pas`:
- Around line 6158-6159: The code creates a new TGocciaSourcePipeline but resets
PipelineOptions.Preprocessors, PipelineOptions.Compatibility and
PipelineOptions.SourceType to defaults, causing template expressions to be
parsed with different rules; instead reuse the existing active source-pipeline
options by copying the current pipeline’s TGocciaSourcePipelineOptions into the
new PipelineOptions (e.g., assign PipelineOptions :=
CurrentPipeline.PipelineOptions or clone the active
TGocciaSourcePipelineOptions) and only adjust any fields that must differ,
ensuring Preprocessors, Compatibility and SourceType are preserved; apply the
same change in the other occurrence that constructs a TGocciaSourcePipeline so
both sites inherit the surrounding parser policy.

---

Outside diff comments:
In `@source/units/Goccia.SourcePipeline.pas`:
- Around line 394-425: ParseExpression currently lexes AExpressionText directly
(creating SourceLines and TGocciaLexer with the raw text) so preprocessors
(e.g., JSX handling) are skipped; update TGocciaSourcePipeline.ParseExpression
to run the expression text through ApplyPreprocessors (using the provided
AOptions) before calling CreateECMAScriptSourceLines and TGocciaLexer.Create,
then proceed to ConfigureParser and parsing as before so
Parser.ParseExpressionWithPrivateNames / Parser.Expression operate on the
preprocessed text.
🪄 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: 57d65d58-371a-47a7-ae68-b1611ff6b05e

📥 Commits

Reviewing files that changed from the base of the PR and between cb9a750 and c24447e.

📒 Files selected for processing (6)
  • scripts/test-cli.ts
  • source/shared/CLI.Parser.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.SourcePipeline.pas

Comment thread source/units/Goccia.Engine.pas
Comment thread source/units/Goccia.Evaluator.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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (3)
source/app/GocciaScriptLoader.dpr (1)

630-647: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Register coverage against the generated source, not ASource.

This path now parses through TGocciaSourcePipeline, but coverage still counts executable lines from the original ASource. For JSX/preprocessed inputs that can disagree with the cloned source map and produce incorrect coverage metadata. Thread GeneratedSourceLines out of ParseSource (or keep the pipeline result alive here) and use that for RegisterSourceFile.

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

In `@source/app/GocciaScriptLoader.dpr` around lines 630 - 647, ParseSource is
returning a transformed/generated source via the pipeline but the coverage
registration is still using the original ASource (used in CountExecutableLines
and RegisterSourceFile), which yields wrong line counts for preprocessed/JSX
inputs; update ParseSource (or its caller) to return or expose the generated
source lines (e.g., GeneratedSourceLines or keep the pipeline result alive) and
replace uses of ASource in TGocciaCoverageTracker.Instance.RegisterSourceFile
and CountExecutableLines with the generated source (and pass SourceMap.Clone as
before) so coverage is counted against the generated output rather than the
original ASource.
source/shared/CLI.Parser.pas (1)

92-100: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Allow dash-prefixed separate values.

Line 96 treats any next token starting with - as “missing”, so valid argv like --timeout -1 or --output --tmp-file never reaches Option.Apply. That makes separate-value options parse incorrectly for a real class of inputs.

Suggested fix
-          if (I >= Count) or
-             (Copy(ParamStr(I + 1), 1, 1) = SHORT_FLAG_CHAR) then
+          if I >= Count then
             raise TParseError.CreateFmt(
               '--%s requires a value', [Name]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/shared/CLI.Parser.pas` around lines 92 - 100, The parser currently
rejects any separate value that begins with SHORT_FLAG_CHAR (treating e.g.
"--timeout -1" as missing); update the check in the block handling
Option.ConsumesSeparateValue so the next token is considered a flag only when it
really looks like one (e.g. starts with "--" or starts with "-" followed by a
non-digit), otherwise accept it as the option's value; specifically change the
condition that inspects ParamStr(I+1) to treat "--..." as a flag but allow "-1"
or "-tmp" as values by checking Copy(ParamStr(I+1),1,2) = '--' or
(Copy(...,1,1)=SHORT_FLAG_CHAR and not (ParamStr(I+1)[2] in ['0'..'9'])), then
proceed to Inc(I); set Value := ParamStr(I) and call Option.Apply as before
(symbols: Option.ConsumesSeparateValue, ParamStr, SHORT_FLAG_CHAR, Value,
Inc(I), Option.Apply, TParseError.CreateFmt).
source/units/Goccia.SourcePipeline.pas (1)

549-550: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't free the preprocessor source map before parse errors are translated.

SourceMap.Free runs before ParseUnchecked, so any TGocciaError raised here reports positions in the transformed wrapper source when preprocessing is active. That regresses diagnostics for JSX-enabled dynamic Function parsing compared with Parse, which keeps the map alive until after error translation.

Also applies to: 552-558

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

In `@source/units/Goccia.SourcePipeline.pas` around lines 549 - 550, The SourceMap
created by ApplyPreprocessors is being freed too early (SourceMap.Free runs
before ParseUnchecked), causing ParseUnchecked to raise TGocciaError with
positions in the transformed wrapper instead of original source; update the flow
so that the SourceMap instance returned by ApplyPreprocessors is not freed until
after ParseUnchecked completes and after any error translation/position
remapping logic has run (mirror how Parse keeps the map alive); specifically,
delay or move the SourceMap.Free call to after the error-translation step that
follows ParseUnchecked (also apply the same change to the analogous block around
the 552–558 region) so diagnostics use the original-source positions.
♻️ Duplicate comments (1)
source/units/Goccia.Engine.pas (1)

1269-1327: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Temp-root the script result across WaitForRuntimeIdle.

The module branch now protects its return value before draining pending work, but the script branch still does FExecutor.ExecuteProgram(...) followed by WaitForRuntimeIdle without rooting that value first. If a pending job triggers GC, FLastTiming.Result can be reclaimed before Execute returns it.

Suggested fix
         CheckTopLevelRedeclarations(PipelineResult.ProgramNode,
           FInterpreter.GlobalScope, FSourcePath);
-        FLastTiming.Result := FExecutor.ExecuteProgram(
-          PipelineResult.ProgramNode);
-        WaitForRuntimeIdle;
+        ModuleResult := FExecutor.ExecuteProgram(
+          PipelineResult.ProgramNode);
+        GC := TGarbageCollector.Instance;
+        if Assigned(ModuleResult) and Assigned(GC) then
+          GC.AddTempRoot(ModuleResult);
+        try
+          WaitForRuntimeIdle;
+          FLastTiming.Result := ModuleResult;
+        finally
+          if Assigned(ModuleResult) and Assigned(GC) then
+            GC.RemoveTempRoot(ModuleResult);
+        end;
         ExecEnd := GetNanoseconds;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Engine.pas` around lines 1269 - 1327, The non-module
(script) branch must protect the execution result across WaitForRuntimeIdle the
same way the module branch does: capture ExecuteProgram's return into a local
(e.g., ScriptResult), get GC := TGarbageCollector.Instance, if
Assigned(ScriptResult) and Assigned(GC) call GC.AddTempRoot(ScriptResult) before
WaitForRuntimeIdle, then assign FLastTiming.Result and finally remove the temp
root in a finally block (GC.RemoveTempRoot(ScriptResult)); mirror the
module-branch pattern around FExecutor.ExecuteProgram, WaitForRuntimeIdle, and
FLastTiming.Result using the same GC checks.
🧹 Nitpick comments (2)
source/shared/CLI.Parser.Test.pas (1)

142-156: ⚡ Quick win

Test parser behavior here, not the option internals.

This only proves TOptionalStringOption reports ConsumesSeparateValue = False; it does not prove ParseCommandLine actually leaves the following argv token untouched. A parser-level test for --source-map, --source-map=out.map, and a trailing positional arg would cover the changed contract much better.

As per coding guidelines, **/*.pas: Keep implementation details private; test only public APIs.

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

In `@source/shared/CLI.Parser.Test.pas` around lines 142 - 156, The current unit
TestOptionalStringOptionDoesNotConsumeSeparateValue inspects
TOptionalStringOption internals instead of exercising the parser contract;
replace or augment it with a parser-level test that calls ParseCommandLine (or
the public CLI parsing entrypoint used in tests) using argv scenarios:
["--source-map", "positional"], ["--source-map=out.map", "positional"], and
ensure the parser marks the option present with the correct Value (empty for
bare --source-map, "out.map" for the = form) and that the trailing positional
argument remains in the parser's positional results; target the public API
(ParseCommandLine) and avoid asserting on TOptionalStringOption.
source/units/Goccia.SourcePipeline.pas (1)

31-35: 🏗️ Heavy lift

Keep the active-options stack off the public surface.

TGocciaSourcePipelineOptionsFrame plus PushActiveOptions/PopActiveOptions exposes the thread-local bookkeeping protocol as a supported API. That looks like an internal propagation mechanism, not something callers should couple to directly. A scoped helper/internal hook would keep this unit's public surface narrower.

As per coding guidelines, **/*.pas: Keep implementation details private; test only public APIs`.

Also applies to: 100-105

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

In `@source/units/Goccia.SourcePipeline.pas` around lines 31 - 35,
TGocciaSourcePipelineOptionsFrame and the PushActiveOptions/PopActiveOptions
protocol are implementation details and should be removed from the unit's public
interface; move the TGocciaSourcePipelineOptionsFrame type and the
PushActiveOptions/PopActiveOptions routines into the implementation section (or
hide them inside a non-exported helper), and replace any external usage with a
scoped/internal RAII-style helper (e.g. a local record/class with
constructor/destructor that calls PushActiveOptions/PopActiveOptions) so callers
only see the public API and not the thread-local bookkeeping symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/shared/CLI.Options.pas`:
- Around line 76-80: TOptionalStringOption must clear the inherited
TStringOption stored value when the flag is present without an explicit "=..."
value; to fix, implement ConsumesSeparateValue and the option-handling path so
that when the parser recognizes the option with no separate value you explicitly
set the underlying string storage to empty (clear TStringOption's FValue or call
its setter to set '') and mark the option as present, and keep FormatForHelp
as-is for help text; locate TOptionalStringOption and add the no-value handling
in the code path the parser uses (the override of ConsumesSeparateValue and/or
the SetValue/assign hook used by TStringOption) so a bare "--source-map" clears
the inherited value instead of leaving it.

---

Outside diff comments:
In `@source/app/GocciaScriptLoader.dpr`:
- Around line 630-647: ParseSource is returning a transformed/generated source
via the pipeline but the coverage registration is still using the original
ASource (used in CountExecutableLines and RegisterSourceFile), which yields
wrong line counts for preprocessed/JSX inputs; update ParseSource (or its
caller) to return or expose the generated source lines (e.g.,
GeneratedSourceLines or keep the pipeline result alive) and replace uses of
ASource in TGocciaCoverageTracker.Instance.RegisterSourceFile and
CountExecutableLines with the generated source (and pass SourceMap.Clone as
before) so coverage is counted against the generated output rather than the
original ASource.

In `@source/shared/CLI.Parser.pas`:
- Around line 92-100: The parser currently rejects any separate value that
begins with SHORT_FLAG_CHAR (treating e.g. "--timeout -1" as missing); update
the check in the block handling Option.ConsumesSeparateValue so the next token
is considered a flag only when it really looks like one (e.g. starts with "--"
or starts with "-" followed by a non-digit), otherwise accept it as the option's
value; specifically change the condition that inspects ParamStr(I+1) to treat
"--..." as a flag but allow "-1" or "-tmp" as values by checking
Copy(ParamStr(I+1),1,2) = '--' or (Copy(...,1,1)=SHORT_FLAG_CHAR and not
(ParamStr(I+1)[2] in ['0'..'9'])), then proceed to Inc(I); set Value :=
ParamStr(I) and call Option.Apply as before (symbols:
Option.ConsumesSeparateValue, ParamStr, SHORT_FLAG_CHAR, Value, Inc(I),
Option.Apply, TParseError.CreateFmt).

In `@source/units/Goccia.SourcePipeline.pas`:
- Around line 549-550: The SourceMap created by ApplyPreprocessors is being
freed too early (SourceMap.Free runs before ParseUnchecked), causing
ParseUnchecked to raise TGocciaError with positions in the transformed wrapper
instead of original source; update the flow so that the SourceMap instance
returned by ApplyPreprocessors is not freed until after ParseUnchecked completes
and after any error translation/position remapping logic has run (mirror how
Parse keeps the map alive); specifically, delay or move the SourceMap.Free call
to after the error-translation step that follows ParseUnchecked (also apply the
same change to the analogous block around the 552–558 region) so diagnostics use
the original-source positions.

---

Duplicate comments:
In `@source/units/Goccia.Engine.pas`:
- Around line 1269-1327: The non-module (script) branch must protect the
execution result across WaitForRuntimeIdle the same way the module branch does:
capture ExecuteProgram's return into a local (e.g., ScriptResult), get GC :=
TGarbageCollector.Instance, if Assigned(ScriptResult) and Assigned(GC) call
GC.AddTempRoot(ScriptResult) before WaitForRuntimeIdle, then assign
FLastTiming.Result and finally remove the temp root in a finally block
(GC.RemoveTempRoot(ScriptResult)); mirror the module-branch pattern around
FExecutor.ExecuteProgram, WaitForRuntimeIdle, and FLastTiming.Result using the
same GC checks.

---

Nitpick comments:
In `@source/shared/CLI.Parser.Test.pas`:
- Around line 142-156: The current unit
TestOptionalStringOptionDoesNotConsumeSeparateValue inspects
TOptionalStringOption internals instead of exercising the parser contract;
replace or augment it with a parser-level test that calls ParseCommandLine (or
the public CLI parsing entrypoint used in tests) using argv scenarios:
["--source-map", "positional"], ["--source-map=out.map", "positional"], and
ensure the parser marks the option present with the correct Value (empty for
bare --source-map, "out.map" for the = form) and that the trailing positional
argument remains in the parser's positional results; target the public API
(ParseCommandLine) and avoid asserting on TOptionalStringOption.

In `@source/units/Goccia.SourcePipeline.pas`:
- Around line 31-35: TGocciaSourcePipelineOptionsFrame and the
PushActiveOptions/PopActiveOptions protocol are implementation details and
should be removed from the unit's public interface; move the
TGocciaSourcePipelineOptionsFrame type and the
PushActiveOptions/PopActiveOptions routines into the implementation section (or
hide them inside a non-exported helper), and replace any external usage with a
scoped/internal RAII-style helper (e.g. a local record/class with
constructor/destructor that calls PushActiveOptions/PopActiveOptions) so callers
only see the public API and not the thread-local bookkeeping symbols.
🪄 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: 34572e79-5635-4145-b287-5ed4b32a7c0b

📥 Commits

Reviewing files that changed from the base of the PR and between c24447e and 61aa622.

📒 Files selected for processing (9)
  • source/app/GocciaBundler.dpr
  • source/app/GocciaScriptLoader.dpr
  • source/shared/CLI.Options.pas
  • source/shared/CLI.Parser.Test.pas
  • source/shared/CLI.Parser.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.SourcePipeline.pas

Comment thread source/shared/CLI.Options.pas
frostney added 2 commits May 25, 2026 00:49
- Let required options accept dash-prefixed values
- Preserve generated source lines for coverage and source maps
- Make pipeline options scope-based and keep script results rooted
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

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.Modules.Loader.pas (1)

402-410: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don't print module-parse warnings directly from the loader.

These WriteLn calls bypass the CLI's warning suppression/capture path, so an imported-module warning will still hit stdout in --output=json flows and can corrupt the JSON stream. Please route these warnings through the same sink used by entry-source parsing instead of emitting them here.

Based on learnings: For GocciaScript CLI tools, suppress human-readable status/progress WriteLn output when running in JSON output mode.

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

In `@source/units/Goccia.Modules.Loader.pas` around lines 402 - 410, The loader
currently prints ModuleParseResult warnings directly via WriteLn (iterating
ModuleParseResult.Warnings and printing ModuleWarning.Message/Suggestion and
ResolvedPath), which bypasses the CLI's JSON-safe warning sink; replace those
WriteLn calls with the same warning-emission API used by entry-source parsing
(i.e. call the centralized warning/logger method rather than WriteLn),
forwarding the message, suggestion (if any), and location (ResolvedPath,
ModuleWarning.Line, ModuleWarning.Column) so warnings respect JSON/output modes
and suppression.
♻️ Duplicate comments (1)
source/units/Goccia.SourcePipeline.pas (1)

481-499: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply preprocessors in ParseExpression too.

This helper still lexes AExpressionText directly, so AOptions.Preprocessors are ignored here. JSX expressions will parse through Parse/ParseDynamicFunctionWrapper but fail through ParseExpression, which makes the new pipeline API inconsistent.

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

In `@source/units/Goccia.SourcePipeline.pas` around lines 481 - 499,
ParseExpression currently lexes AExpressionText directly and ignores
AOptions.Preprocessors, causing JSX and other preprocessed syntax to fail; run
the same preprocessing step used by Parse/ParseDynamicFunctionWrapper on
AExpressionText before creating SourceLines and the TGocciaLexer so
preprocessors are applied. Specifically, replace uses of AExpressionText in
CreateECMAScriptSourceLines and TGocciaLexer.Create inside ParseExpression with
the preprocessed text (apply AOptions.Preprocessors to AExpressionText first),
then continue to call ConfigureParser(Parser, AOptions) and the existing parser
methods (ParseExpressionWithPrivateNames / Expression) as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Engine.pas`:
- Line 1242: The temporary rooting of the ScriptResult must be moved into the
public TGocciaEngine.ExecuteProgram so the TGocciaValue returned across
WaitForRuntimeIdle is protected from GC; locate ExecuteProgram and add a
Root/Unroot (or equivalent TempRoot/TempUnroot) around the value returned
(reusing the same rooting logic currently next to ScriptResult), ensure
WaitForRuntimeIdle is called while the value is rooted, and remove the duplicate
local rooting where ScriptResult is declared so both internal and external
callers are protected; reference the ExecuteProgram method, the ScriptResult
variable, and the WaitForRuntimeIdle call when making the change.

---

Outside diff comments:
In `@source/units/Goccia.Modules.Loader.pas`:
- Around line 402-410: The loader currently prints ModuleParseResult warnings
directly via WriteLn (iterating ModuleParseResult.Warnings and printing
ModuleWarning.Message/Suggestion and ResolvedPath), which bypasses the CLI's
JSON-safe warning sink; replace those WriteLn calls with the same
warning-emission API used by entry-source parsing (i.e. call the centralized
warning/logger method rather than WriteLn), forwarding the message, suggestion
(if any), and location (ResolvedPath, ModuleWarning.Line, ModuleWarning.Column)
so warnings respect JSON/output modes and suppression.

---

Duplicate comments:
In `@source/units/Goccia.SourcePipeline.pas`:
- Around line 481-499: ParseExpression currently lexes AExpressionText directly
and ignores AOptions.Preprocessors, causing JSX and other preprocessed syntax to
fail; run the same preprocessing step used by Parse/ParseDynamicFunctionWrapper
on AExpressionText before creating SourceLines and the TGocciaLexer so
preprocessors are applied. Specifically, replace uses of AExpressionText in
CreateECMAScriptSourceLines and TGocciaLexer.Create inside ParseExpression with
the preprocessed text (apply AOptions.Preprocessors to AExpressionText first),
then continue to call ConfigureParser(Parser, AOptions) and the existing parser
methods (ParseExpressionWithPrivateNames / Expression) as before.
🪄 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: 7cdfacff-ade1-4024-a588-0059ee9e25b4

📥 Commits

Reviewing files that changed from the base of the PR and between e79e426 and 295ac52.

⛔ Files ignored due to path filters (32)
  • tmp/logo-detail/cut.png is excluded by !**/*.png
  • tmp/logo-detail/final-balanced.png is excluded by !**/*.png
  • tmp/logo-detail/manual-balanced.png is excluded by !**/*.png
  • tmp/logo-detail/manual-balanced.svg is excluded by !**/*.svg
  • tmp/logo-detail/manual-max.png is excluded by !**/*.png
  • tmp/logo-detail/manual-max.svg is excluded by !**/*.svg
  • tmp/logo-detail/photo-preset.png is excluded by !**/*.png
  • tmp/logo-detail/photo-preset.svg is excluded by !**/*.svg
  • tmp/logo-large.svg is excluded by !**/*.svg
  • tmp/logo-medium.svg is excluded by !**/*.svg
  • tmp/logo-poster.svg is excluded by !**/*.svg
  • tmp/logo-small.svg is excluded by !**/*.svg
  • tmp/logo-tune/current-script-output.png is excluded by !**/*.png
  • tmp/logo-tune/cut-detail-no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/cut-detail-soft.png is excluded by !**/*.png
  • tmp/logo-tune/cut-floodfill.png is excluded by !**/*.png
  • tmp/logo-tune/cut-max-detail.png is excluded by !**/*.png
  • tmp/logo-tune/cut-no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/cut-preserve-alpha.png is excluded by !**/*.png
  • tmp/logo-tune/detail-no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/detail-no-blur.svg is excluded by !**/*.svg
  • tmp/logo-tune/detail-soft.png is excluded by !**/*.png
  • tmp/logo-tune/detail-soft.svg is excluded by !**/*.svg
  • tmp/logo-tune/final-logo-svg-render.png is excluded by !**/*.png
  • tmp/logo-tune/floodfill.png is excluded by !**/*.png
  • tmp/logo-tune/floodfill.svg is excluded by !**/*.svg
  • tmp/logo-tune/max-detail.png is excluded by !**/*.png
  • tmp/logo-tune/max-detail.svg is excluded by !**/*.svg
  • tmp/logo-tune/no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/no-blur.svg is excluded by !**/*.svg
  • tmp/logo-tune/preserve-alpha.png is excluded by !**/*.png
  • tmp/logo-tune/preserve-alpha.svg is excluded by !**/*.svg
📒 Files selected for processing (6)
  • source/app/GocciaScriptLoader.dpr
  • source/shared/CLI.Parser.Test.pas
  • source/shared/CLI.Parser.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.SourcePipeline.pas

Comment thread source/units/Goccia.Engine.pas Outdated
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.Engine.pas (1)

1249-1269: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the module loader in sync with the engine’s parser policy.

This entry path now parses and activates FPreprocessors / FCompatibility, but imported modules still read TGocciaModuleLoader.FPreprocessors / FCompatibility. Those loader fields are never initialized from the engine defaults and SetPreprocessors / SetCompatibility do not update them, so imports can silently parse with stale policy. The default case already regresses JSX: the entry file uses [ppJSX], while imported modules stay at [].

🩹 Suggested sync points
 procedure TGocciaEngine.Initialize(const AFileName: string;
   const ASourceLines: TStringList; const AModuleLoader: TGocciaModuleLoader;
   const AOwnsModuleLoader: Boolean);
 begin
   ...
   FPreprocessors := DefaultPreprocessors;
   FCompatibility := DefaultCompatibility;
+  FModuleLoader.Preprocessors := FPreprocessors;
+  FModuleLoader.Compatibility := FCompatibility;
   ...
 end;

 procedure TGocciaEngine.SetPreprocessors(const AValue: TGocciaPreprocessors);
 begin
   FPreprocessors := AValue;
+  if Assigned(FModuleLoader) then
+    FModuleLoader.Preprocessors := AValue;
   FInterpreter.JSXEnabled := ppJSX in AValue;
 end;

 procedure TGocciaEngine.SetCompatibility(const AValue: TGocciaCompatibilityFlags);
 begin
   FCompatibility := AValue;
+  if Assigned(FModuleLoader) then
+    FModuleLoader.Compatibility := AValue;
   FInterpreter.ASIEnabled := cfASI in AValue;
   ...
 end;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Engine.pas` around lines 1249 - 1269, The module loader
still uses TGocciaModuleLoader.FPreprocessors/FCompatibility which are never
initialized/updated, so imported modules can parse with stale policy; to fix,
propagate the engine's FPreprocessors and FCompatibility into the module loader
before parsing and whenever the engine options change: assign
TGocciaModuleLoader.FPreprocessors := FPreprocessors and
TGocciaModuleLoader.FCompatibility := FCompatibility right before calling
TGocciaSourcePipeline.Parse (or when creating the loader) and also update
SetPreprocessors and SetCompatibility to write through to TGocciaModuleLoader so
imports always use the current policy.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Engine.pas`:
- Around line 1314-1316: The runtime cleanup (clearing microtask queue and
calling DiscardRuntimePending -> TGocciaEngineExtension.DiscardPending) is
executed in both Execute and ExecuteProgram, causing duplicate cleanup; pick a
single owner by removing the cleanup from the delegated routine: keep the
finally cleanup in Execute (the top-level caller) and remove the finally block
that clears microtasks and calls DiscardRuntimePending from ExecuteProgram (also
update the other duplicate region around the second occurrence), ensuring only
Execute performs the microtask queue clear and calls DiscardRuntimePending so
DiscardPending runs exactly once per script execution.

In `@source/units/Goccia.SourcePipeline.pas`:
- Around line 97-98: The ActivateOptions function must stop using a simple
restore-by-snapshot IInterface and instead push the AOptions onto a thread-local
stack; return an IInterface wrapper (e.g. a small TInterfacedObject scope token)
whose destructor performs a guarded pop: it only restores the previous options
if the token is the current top of the stack, otherwise it removes itself from
the middle without touching the current top. Update ActivateOptions to Push the
new options onto the TLS stack and return that token; implement the token's
Release/Destroy to check identity against the stack top (and unlink if
out-of-order) so out-of-order scope releases cannot clobber active options.
Ensure the same pattern is applied at the other restore-by-snapshot call sites
noted (lines 137-149, 217-246, 341-345) so every scope uses the top-of-stack
guard/unlink strategy.

---

Outside diff comments:
In `@source/units/Goccia.Engine.pas`:
- Around line 1249-1269: The module loader still uses
TGocciaModuleLoader.FPreprocessors/FCompatibility which are never
initialized/updated, so imported modules can parse with stale policy; to fix,
propagate the engine's FPreprocessors and FCompatibility into the module loader
before parsing and whenever the engine options change: assign
TGocciaModuleLoader.FPreprocessors := FPreprocessors and
TGocciaModuleLoader.FCompatibility := FCompatibility right before calling
TGocciaSourcePipeline.Parse (or when creating the loader) and also update
SetPreprocessors and SetCompatibility to write through to TGocciaModuleLoader so
imports always use the current policy.
🪄 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: 041a3056-0316-4d22-8647-56ee64511657

📥 Commits

Reviewing files that changed from the base of the PR and between e79e426 and 1ee8948.

⛔ Files ignored due to path filters (32)
  • tmp/logo-detail/cut.png is excluded by !**/*.png
  • tmp/logo-detail/final-balanced.png is excluded by !**/*.png
  • tmp/logo-detail/manual-balanced.png is excluded by !**/*.png
  • tmp/logo-detail/manual-balanced.svg is excluded by !**/*.svg
  • tmp/logo-detail/manual-max.png is excluded by !**/*.png
  • tmp/logo-detail/manual-max.svg is excluded by !**/*.svg
  • tmp/logo-detail/photo-preset.png is excluded by !**/*.png
  • tmp/logo-detail/photo-preset.svg is excluded by !**/*.svg
  • tmp/logo-large.svg is excluded by !**/*.svg
  • tmp/logo-medium.svg is excluded by !**/*.svg
  • tmp/logo-poster.svg is excluded by !**/*.svg
  • tmp/logo-small.svg is excluded by !**/*.svg
  • tmp/logo-tune/current-script-output.png is excluded by !**/*.png
  • tmp/logo-tune/cut-detail-no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/cut-detail-soft.png is excluded by !**/*.png
  • tmp/logo-tune/cut-floodfill.png is excluded by !**/*.png
  • tmp/logo-tune/cut-max-detail.png is excluded by !**/*.png
  • tmp/logo-tune/cut-no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/cut-preserve-alpha.png is excluded by !**/*.png
  • tmp/logo-tune/detail-no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/detail-no-blur.svg is excluded by !**/*.svg
  • tmp/logo-tune/detail-soft.png is excluded by !**/*.png
  • tmp/logo-tune/detail-soft.svg is excluded by !**/*.svg
  • tmp/logo-tune/final-logo-svg-render.png is excluded by !**/*.png
  • tmp/logo-tune/floodfill.png is excluded by !**/*.png
  • tmp/logo-tune/floodfill.svg is excluded by !**/*.svg
  • tmp/logo-tune/max-detail.png is excluded by !**/*.png
  • tmp/logo-tune/max-detail.svg is excluded by !**/*.svg
  • tmp/logo-tune/no-blur.png is excluded by !**/*.png
  • tmp/logo-tune/no-blur.svg is excluded by !**/*.svg
  • tmp/logo-tune/preserve-alpha.png is excluded by !**/*.png
  • tmp/logo-tune/preserve-alpha.svg is excluded by !**/*.svg
📒 Files selected for processing (6)
  • source/app/GocciaScriptLoader.dpr
  • source/shared/CLI.Parser.Test.pas
  • source/shared/CLI.Parser.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.SourcePipeline.pas

Comment thread source/units/Goccia.Engine.pas
Comment thread source/units/Goccia.SourcePipeline.pas Outdated
@frostney frostney merged commit 113fa59 into main May 25, 2026
14 checks passed
@frostney frostney deleted the more-docs-changes branch May 25, 2026 07:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation internal Refactoring, CI, tooling, cleanup new feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant