Skip to content

Add TOML parsing, globals, and compliance checks#183

Merged
frostney merged 3 commits intomainfrom
feature/toml-support
Apr 5, 2026
Merged

Add TOML parsing, globals, and compliance checks#183
frostney merged 3 commits intomainfrom
feature/toml-support

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 4, 2026

Summary

  • Add TOML.parse(...) as a first-class built-in and wire TOML into module imports and CLI globals loading.
  • Introduce a standalone TOML parser utility plus a GocciaTOMLCheck compliance harness for the official toml-test 1.1.0 corpus.
  • Update docs, built-in listings, language restrictions, and build/CI flow to reflect TOML support and conformance checks.
  • Treat TOML date/time values as validated strings at the runtime surface while preserving typed values for compliance verification.

Fixes #177

Testing

  • CI now runs python3 scripts/run_toml_test_suite.py --harness=./build/GocciaTOMLCheck across the platform matrix.
  • TOML compliance is gated on zero failed, false_accepts, false_rejects, valid_mismatches, and timeouts in the generated JSON summary.
  • Existing JS and Pascal test jobs remain part of the pipeline via ./build/TestRunner tests and native unit-test execution.

Summary by CodeRabbit

  • New Features

    • Added TOML support: TOML.parse, importing .toml modules, and accepting TOML for --globals; TOML builtin enabled by default.
  • Documentation

    • Updated README and docs with TOML examples, CLI/global usage, runtime semantics, conformance instructions, and embedding notes.
  • Tests

    • Added unit tests for TOML parsing and import behavior; added test-suite runner for TOML conformance.
  • Chores

    • CI now builds a TOML checker, runs per-platform TOML compliance, and uploads JSON reports.

- Add TOML built-ins, module imports, and globals injection
- Add TOML test harness and toml-test CI job
- Update docs and runtime wiring for TOML support
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 4, 2026

📝 Walkthrough

Walkthrough

Adds end-to-end TOML support: parser unit and AST, TOML builtin and globals injection, .toml module imports, parser tests and harness, Jest module/builtin tests, docs updates, and CI toml-compliance workflow that builds and runs a GocciaTOMLCheck harness against toml-test v1.1.0.

Changes

Cohort / File(s) Summary
CI Workflow & Artifacts
.github/workflows/ci.yml
Cross-compile and stage GocciaTOMLCheck per target; add toml-compliance matrix job that downloads artifacts, runs run_toml_test_suite.py with platform harness, validates summary counters, uploads per-target JSON, and gates downstream jobs on success.
TOML Parser & AST
units/Goccia.TOML.pas
New comprehensive TOML parser unit with AST node model, scalar-kind tracking, ParseDocument/Parse APIs, canonicalization, and EGocciaTOMLParseError.
Builtins & Runtime Integration
units/Goccia.Builtins.TOML.pas, units/Goccia.Engine.pas, units/Goccia.Runtime.Bootstrap.pas, units/Goccia.Engine.Backend.pas
Added TGocciaTOMLBuiltin (TOML.parse), ggTOML flag and default inclusion, engine/backend InjectGlobalsFromTOML, runtime bootstrap wiring, and central RequireGlobalsBootstrapReady validation.
Module Loader & File Extensions
units/Goccia.Modules.Loader.pas, units/Goccia.FileExtensions.pas, units/Goccia.ScriptLoader.Globals.pas, ScriptLoader.dpr
Recognize .toml via EXT_TOML/IsTOMLExtension/IsTOMLGlobalsFile; parse .toml modules equivalently to JSON, added ScriptLoader dispatch and CLI help update for JSON/TOML/YAML globals.
Tests, Harness & Runners
scripts/GocciaTOMLCheck.dpr, scripts/run_toml_test_suite.py, units/Goccia.TOML.Test.pas, units/Goccia.ScriptLoader.Globals.Test.pas
New Delphi harness that serializes parsed TOML, Python runner to execute the official toml-test suite and produce JSON reports, and Pascal unit tests for parser and globals injection.
JS Tests & Fixtures
tests/built-ins/TOML/parse.js, tests/language/modules/toml-import.js, tests/language/modules/helpers/config.toml
Added Jest tests covering TOML.parse and module imports plus example TOML fixture.
Docs & Guides
README.md, AGENTS.md, docs/*.md
Documented TOML runtime API (TOML.parse), CLI globals/file extension behavior, module import semantics, testing/CI instructions, and added ggTOML/ggYAML to defaults.
Utilities & Ignores
.gitignore
Ignore scripts/__pycache__/.

Sequence Diagram(s)

sequenceDiagram
  participant CI as "GitHub Actions"
  participant Build as "Build Job (fpc-cross)"
  participant Artifact as "Artifact Storage"
  participant TomlJob as "toml-compliance (matrix)"
  participant Runner as "run_toml_test_suite.py"
  participant Harness as "GocciaTOMLCheck"

  CI->>Build: trigger cross-compile (include GocciaTOMLCheck per target)
  Build->>Artifact: upload `gocciascript-${target}` and `GocciaTOMLCheck`
  CI->>TomlJob: start per-target matrix job
  TomlJob->>Artifact: download `gocciascript-${target}` and `GocciaTOMLCheck`
  TomlJob->>Harness: select platform harness binary
  TomlJob->>Runner: run `run_toml_test_suite.py --harness=./build/GocciaTOMLCheck`
  Runner->>Harness: invoke harness per-case, collect results
  Runner->>TomlJob: write `toml-test-results-<target>.json`
  TomlJob->>Artifact: upload per-target JSON artifact
  CI->>CI: downstream jobs wait for toml-compliance success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through tables, arrays, and time,

I kept each scalar, canonical and prime.
A harness built and tests that run,
TOML now speaks and CI gives one—
Hooray, the configs parse just fine! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: adding TOML parsing, globals injection, and compliance checks. It accurately summarizes the primary contribution.
Linked Issues check ✅ Passed All coding requirements from issue #177 are met: TOML.parse builtin [units/Goccia.Builtins.TOML.pas], .toml module imports [units/Goccia.Modules.Loader.pas], CLI globals support [ScriptLoader.dpr, units/Goccia.Engine.pas], file extension recognition [units/Goccia.FileExtensions.pas], and comprehensive tests [tests/built-ins/TOML/parse.js, tests/language/modules/toml-import.js].
Out of Scope Changes check ✅ Passed All changes align with PR objectives. CI workflow updates, documentation additions, compliance harness implementation (GocciaTOMLCheck.dpr, run_toml_test_suite.py), bootstrap/engine modifications, and test additions are all directly supporting TOML support and conformance goals.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/toml-support

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

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 4, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 3654 3654
Tests Passed 3613 ✅ 3654 ✅
Tests Skipped 41 0
Tests Execution 203.7ms 197.8ms
Tests Engine 375.4ms 692.7ms
Benchmarks Total 274 274
Benchmarks Duration 7.51min 6.38min

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 4, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 80 improved · 🔴 141 regressed · 53 unchanged · avg -1.5%
Bytecode: 🟢 135 improved · 🔴 32 regressed · 107 unchanged · avg +3.0%

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

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.

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

🧹 Nitpick comments (3)
units/Goccia.ScriptLoader.Globals.Test.pas (1)

30-37: Add one CLI-level TOML globals smoke test too.

These tests stop at TGocciaEngine / TGocciaBytecodeBackend, so they won't catch regressions in the actual ScriptLoader --globals=...toml dispatch path. The new format is user-facing and should get the same command-level coverage the workflow already gives JSON/module globals.

As per coding guidelines "When a feature is exposed through a CLI tool or other documented user-facing API, prefer adding or extending command-level/workflow-level coverage instead of asserting internal helper behavior directly in Pascal tests".

Also applies to: 53-113

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

In `@units/Goccia.ScriptLoader.Globals.Test.pas` around lines 30 - 37, Add a
CLI-level smoke test to SetupTests by registering a new test (e.g., Test('CLI
injects globals from TOML', TestCLIInjectGlobalsFromTOML)) alongside
TestDetectsTOMLGlobalsFileByExtension, TestEngineInjectGlobalsFromTOML and
TestBytecodeBackendInjectGlobalsFromTOML; implement TestCLIInjectGlobalsFromTOML
to invoke the ScriptLoader CLI entrypoint with the --globals=<toml file>
argument (using the same helper the JSON/module CLI tests use), assert the
process returns success and that globals were injected into the running script,
and clean up any temp files so the test exercises the full dispatch path from
ScriptLoader to TGocciaEngine/TGocciaBytecodeBackend rather than only testing
internals.
units/Goccia.TOML.pas (2)

444-466: Ownership transfer pattern in ParseDocument is correct but subtle.

The try-finally correctly handles both success and exception cases: on success, FRoot := nil prevents the finally block from freeing the returned node; on exception, FRoot.Free cleans up the partially-constructed tree. Consider adding a brief comment explaining this ownership transfer pattern for future maintainers.

📝 Optional clarifying comment
   try
     SkipBlankLinesAndComments;
     while not IsAtEnd do
     begin
       if PeekChar = '[' then
         ParseTableHeader
       else
         ParseKeyValuePair;
       SkipBlankLinesAndComments;
     end;
+    // Transfer ownership to caller; nil prevents finally from freeing
     Result := FRoot;
     FRoot := nil;
   finally
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.TOML.pas` around lines 444 - 466, Add a short clarifying comment
inside TGocciaTOMLParser.ParseDocument explaining the ownership-transfer
pattern: note that on success the method assigns Result := FRoot and sets FRoot
:= nil to avoid freeing the returned node in the finally block, while on
exception the finally block calls FRoot.Free to clean up the partially-built
tree; reference the Reset, SkipBlankLinesAndComments, PeekChar,
ParseTableHeader, ParseKeyValuePair and the finally block where FRoot is freed
to show exactly where the transfer occurs.

229-244: String building uses repeated concatenation.

StripUnderscores builds the result string via Result := Result + C in a loop. For typical TOML numeric literals (short strings), this is acceptable. If TOML parsing becomes a hot path in the future, consider using TStringBuffer for O(n) assembly.

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

In `@units/Goccia.TOML.pas` around lines 229 - 244, The current StripUnderscores
function should avoid repeated string concatenation; update it to build the
output in linear time by using a preallocated buffer or a
TStringBuilder/TStringBuffer equivalent instead of appending per-character.
Modify the body of StripUnderscores to reserve the needed capacity and write
characters by index (or use TStringBuilder.Append when C <> '_') so the function
performs O(n) work for typical TOML parsing hotspots.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 302-334: Replace the hardcoded python3 invocations in the TOML
compliance steps with a cross-platform fallback like the examples job uses:
define PYTHON_BIN="$(command -v python3 || command -v python)" near the top of
the step and use "$PYTHON_BIN" instead of python3 for both the
run_toml_test_suite.py call and the embedded here-doc execution; keep existing
HARNESS usage and ensure the here-doc still executes via "$PYTHON_BIN - <<'PY'".
This will match the fallback pattern used elsewhere and work on Windows bash
runners where only python may be present.

In `@AGENTS.md`:
- Line 452: DefaultGlobals example is missing ggYAML so it diverges from the
runtime default set; update the list shown (the array containing ggGlobalNumber,
ggPromise, ggJSON, ggTOML, ggSymbol, ggSet, ggMap, ggPerformance, ggTemporal,
ggJSX, ggArrayBuffer) to include ggYAML as well so it matches the default set
defined in units/Goccia.Engine.pas; ensure ggYAML is inserted in the same
comma-separated form as the other gg* entries.

In `@docs/built-ins.md`:
- Line 176: Update the sentence that documents the toml-test rerun command so it
matches the rest of the PR: include the --harness=./build/GocciaTOMLCheck
argument in the command (i.e., use python3 scripts/run_toml_test_suite.py
--harness=./build/GocciaTOMLCheck) or explicitly state that the harness argument
is optional; modify the text in the same paragraph that currently mentions
python3 scripts/run_toml_test_suite.py to reflect this change so
docs/build-system.md and docs/built-ins.md are consistent.

In `@docs/testing.md`:
- Around line 692-723: Update the two fenced diagram blocks in docs/testing.md
to include the language tag "text" so markdownlint stops flagging them: change
the first triple-backtick block containing the build → test → artifacts diagram
to start with ```text and likewise change the second triple-backtick block under
"PR Workflow" (the build → test / benchmark → PR comment / examples diagram) to
start with ```text; no other content changes are needed.

In `@README.md`:
- Around line 307-308: Remove the hard-coded date and exact counts from the
README sentence that currently includes "2026-04-04" and "680 of 680" (the line
beginning "The TOML surface is now also checked..."); replace it with an
evergreen statement such as "The TOML surface is checked against the official
toml-test corpus; see CI or the generated test report for current pass/fail
counts" and keep the local rerun hint (`python3
scripts/run_toml_test_suite.py`). Move the precise snapshot details (date and
numeric results) out of README into CI artifacts, release notes, or a generated
report so the README remains up-to-date without manual edits.

In `@scripts/GocciaTOMLCheck.dpr`:
- Around line 130-145: The harness currently uses TStringList.LoadFromFile and
Source.Text which normalizes line endings and encoding; instead read the file as
a raw byte stream and feed that exact data to the parser. Replace the
TStringList/Source path around Parser.ParseDocument so you use
TFile.ReadAllBytes(ParamStr(1)) (or equivalent raw byte reader), convert the
bytes to a RawByteString/AnsiString without any encoding/CRLF normalization, and
call Parser.ParseDocument (or add a ParseDocumentFromBytes wrapper) with that
raw data; keep the existing variables Parser, Root and error handling intact.

In `@scripts/run_toml_test_suite.py`:
- Around line 77-112: The code currently treats any mapping with keys
{"type","value"} as a tagged scalar; change the guard so it only takes the
tagged-scalar path when both want["type"] and want["value"] are plain strings
(and likewise have["type"] and have["value"] are strings), otherwise fall back
to normal recursive comparison; update the condition around the block that
checks set(want.keys()) == {"type","value"} (and references
want_type/want_value, have_type/have_value and normalize_datetime_text) to
include isinstance checks for both want and have fields before executing the
tagged-scalar logic.
- Around line 35-38: The git clone call currently clones the default branch;
update the argument list in the block that creates temp_dir/checkout_dir and
calls run([...]) so the clone is pinned to the declared SUITE_VERSION (add
"--branch", SUITE_VERSION to the run([...]) arguments, keeping "--depth", "1");
reference the variables temp_dir, checkout_dir and the run(...) invocation when
making the change.
- Around line 223-226: In evaluate_suite, json.loads on stdout or
expected_json_path can raise JSONDecodeError and abort the whole run; wrap the
two json.loads (actual_json = json.loads(stdout or "null") and expected_json =
json.loads(expected_json_path.read_text())) in try/except JSONDecodeError and on
error set ok = False and message to a clear per-case failure note (e.g., "JSON
decode error parsing actual stdout" or "JSON decode error reading expected
fixture: <filename>") so the case is recorded as failed instead of raising; keep
subsequent logic that calls compare_tagged_json unchanged when parsing succeeds
so the summary file is still produced.

In `@units/Goccia.Builtins.TOML.pas`:
- Around line 79-83: The code currently catches all exceptions around
FParser.Parse(...) and converts them to a SyntaxError; change the except block
to only catch the TOML parser's specific exception type (e.g.,
ETOMLParseException or the actual exception class your TOML library uses) and
call ThrowSyntaxError(E.Message) for that case, while re-raising any other
exceptions (use "else raise;" or a plain "raise" for non-parser exceptions);
update the except clause around the
FParser.Parse(AArgs.GetElement(0).ToStringLiteral.Value) call to use "except on
ETOMLParseException do ..." and preserve normal raising for everything else.

In `@units/Goccia.Engine.Backend.pas`:
- Around line 238-264: InjectGlobalsFromTOML can silently no-op because
RegisterGlobal is a no-op when FInterpreter is nil; add a fail-fast guard at the
start of TGocciaBytecodeBackend.InjectGlobalsFromTOML that checks if
FInterpreter is nil and raises a TGocciaRuntimeError (or calls RegisterBuiltIns
if that is the intended bootstrap path) with a clear message about calling
RegisterBuiltIns/bootstrapping first; reference FInterpreter,
InjectGlobalsFromTOML, RegisterGlobal and RegisterBuiltIns so it's easy to
locate and either raise the error or invoke the bootstrap before
parsing/injecting globals.

In `@units/Goccia.TOML.pas`:
- Around line 1541-1542: Replace the use of DefaultFormatSettings in the
TryStrToFloat call with a local TFormatSettings that forces a dot as the decimal
separator: declare a local variable (e.g. FS: TFormatSettings), assign FS :=
DefaultFormatSettings, set FS.DecimalSeparator := '.', and then call
TryStrToFloat(NumberText, AValue, FS) (use the same pattern as in
Goccia.YAML.pas and JSONParser.pas); update the line containing Result :=
TryStrToFloat(NumberText, AValue, DefaultFormatSettings) accordingly.

---

Nitpick comments:
In `@units/Goccia.ScriptLoader.Globals.Test.pas`:
- Around line 30-37: Add a CLI-level smoke test to SetupTests by registering a
new test (e.g., Test('CLI injects globals from TOML',
TestCLIInjectGlobalsFromTOML)) alongside TestDetectsTOMLGlobalsFileByExtension,
TestEngineInjectGlobalsFromTOML and TestBytecodeBackendInjectGlobalsFromTOML;
implement TestCLIInjectGlobalsFromTOML to invoke the ScriptLoader CLI entrypoint
with the --globals=<toml file> argument (using the same helper the JSON/module
CLI tests use), assert the process returns success and that globals were
injected into the running script, and clean up any temp files so the test
exercises the full dispatch path from ScriptLoader to
TGocciaEngine/TGocciaBytecodeBackend rather than only testing internals.

In `@units/Goccia.TOML.pas`:
- Around line 444-466: Add a short clarifying comment inside
TGocciaTOMLParser.ParseDocument explaining the ownership-transfer pattern: note
that on success the method assigns Result := FRoot and sets FRoot := nil to
avoid freeing the returned node in the finally block, while on exception the
finally block calls FRoot.Free to clean up the partially-built tree; reference
the Reset, SkipBlankLinesAndComments, PeekChar, ParseTableHeader,
ParseKeyValuePair and the finally block where FRoot is freed to show exactly
where the transfer occurs.
- Around line 229-244: The current StripUnderscores function should avoid
repeated string concatenation; update it to build the output in linear time by
using a preallocated buffer or a TStringBuilder/TStringBuffer equivalent instead
of appending per-character. Modify the body of StripUnderscores to reserve the
needed capacity and write characters by index (or use TStringBuilder.Append when
C <> '_') so the function performs O(n) work for typical TOML parsing hotspots.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 452c8213-c5c3-4c99-9d28-33d2e7afe8c8

📥 Commits

Reviewing files that changed from the base of the PR and between 837ebf2 and 79183d1.

📒 Files selected for processing (26)
  • .github/workflows/ci.yml
  • .gitignore
  • AGENTS.md
  • README.md
  • ScriptLoader.dpr
  • docs/build-system.md
  • docs/built-ins.md
  • docs/design-decisions.md
  • docs/embedding.md
  • docs/language-restrictions.md
  • docs/testing.md
  • scripts/GocciaTOMLCheck.dpr
  • scripts/run_toml_test_suite.py
  • tests/built-ins/TOML/parse.js
  • tests/language/modules/helpers/config.toml
  • tests/language/modules/toml-import.js
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Engine.Backend.pas
  • units/Goccia.Engine.pas
  • units/Goccia.FileExtensions.pas
  • units/Goccia.Modules.Loader.pas
  • units/Goccia.Runtime.Bootstrap.pas
  • units/Goccia.ScriptLoader.Globals.Test.pas
  • units/Goccia.ScriptLoader.Globals.pas
  • units/Goccia.TOML.Test.pas
  • units/Goccia.TOML.pas

Comment thread .github/workflows/ci.yml
Comment thread AGENTS.md Outdated
Comment thread docs/built-ins.md Outdated
Comment thread docs/testing.md Outdated
Comment thread README.md Outdated
Comment thread scripts/run_toml_test_suite.py Outdated
Comment thread scripts/run_toml_test_suite.py Outdated
Comment thread units/Goccia.Builtins.TOML.pas
Comment thread units/Goccia.Engine.Backend.pas
Comment thread units/Goccia.TOML.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: 3

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

Inline comments:
In `@docs/testing.md`:
- Around line 703-704: Update the docs text for the toml-compliance job to match
the workflow's resolver by noting the Python fallback: change the hardcoded
invocation reference to indicate the job runs scripts/run_toml_test_suite.py
using "python3 (or python on Windows)" or similar wording; reference the
toml-compliance job and the command pattern "python3
scripts/run_toml_test_suite.py --harness=...
--output=toml-test-results-<target>.json" and ensure the doc mentions the
workflow's behavior described in .github/workflows/ci.yml (python3 || python) so
troubleshooting and CI docs stay in sync.

In `@scripts/run_toml_test_suite.py`:
- Around line 110-119: The float comparison branch (using want_type, want_value,
have_value) currently treats -0.0 and 0.0 as equal; after converting to floats
and handling NaNs, add a signed-zero check: import math and when
float(want_lower) == float(have_lower) but both values are zero (e.g.,
float(...) == 0.0), use math.copysign(1.0, float(want_lower)) vs
math.copysign(1.0, float(have_lower)) and if the signs differ return False with
the same error message format (path or '<root>': expected float X, got Y);
otherwise continue to accept the values. Ensure math is imported at the top of
the module.
- Around line 73-81: The normalize_datetime_text function currently returns
datetime/date/time objects which truncate sub-microsecond precision; change it
to return normalized ISO-8601 strings instead (e.g., replace spaces/t/z as done,
adjust trailing Z to +00:00) so callers get the full original precision; update
any consumer that compares fixture values (the test comparison that uses
normalize_datetime_text) to compare these returned strings rather than parsed
datetime/time objects to preserve nanosecond-level differences.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f3fdbd24-b8b4-40e3-af15-3909d3da16fe

📥 Commits

Reviewing files that changed from the base of the PR and between 79183d1 and 7c99662.

📒 Files selected for processing (12)
  • .github/workflows/ci.yml
  • AGENTS.md
  • README.md
  • docs/built-ins.md
  • docs/design-decisions.md
  • docs/embedding.md
  • docs/testing.md
  • scripts/GocciaTOMLCheck.dpr
  • scripts/run_toml_test_suite.py
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Engine.Backend.pas
  • units/Goccia.TOML.pas
✅ Files skipped from review due to trivial changes (3)
  • docs/embedding.md
  • README.md
  • units/Goccia.TOML.pas
🚧 Files skipped from review as they are similar to previous changes (5)
  • AGENTS.md
  • units/Goccia.Engine.Backend.pas
  • docs/built-ins.md
  • units/Goccia.Builtins.TOML.pas
  • docs/design-decisions.md

Comment thread docs/testing.md Outdated
Comment thread scripts/run_toml_test_suite.py Outdated
Comment thread scripts/run_toml_test_suite.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
scripts/run_toml_test_suite.py (1)

350-369: Ensure temporary checkout cleanup runs on exceptions too.

temp_checkout.cleanup() currently runs only on the happy path. If compile_harness(...), evaluate_suite(...), or output writing raises, temp directories can be left behind.

♻️ Proposed refactor
 def main() -> int:
   args = parse_args()
   repo_root = Path(__file__).resolve().parent.parent

   suite_dir, temp_checkout = ensure_suite_checkout(args.suite_dir)
-  if args.harness is not None:
-    harness_path = args.harness.resolve()
-    if not harness_path.is_file():
-      raise FileNotFoundError(f"TOML harness not found: {harness_path}")
-  else:
-    harness_path = compile_harness(repo_root, args.build_dir.resolve())
-  report = evaluate_suite(harness_path, suite_dir.resolve(), args.timeout)
-
-  if args.output is not None:
-    args.output.parent.mkdir(parents=True, exist_ok=True)
-    args.output.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
-
-  print(json.dumps(report["summary"], indent=2))
-  if args.output is not None:
-    print(args.output.resolve())
-
-  if temp_checkout is not None:
-    temp_checkout.cleanup()
-
-  summary = report["summary"]
-  if summary["failed"] > 0 or summary["timeouts"] > 0:
-    return 1
-
-  return 0
+  try:
+    if args.harness is not None:
+      harness_path = args.harness.resolve()
+      if not harness_path.is_file():
+        raise FileNotFoundError(f"TOML harness not found: {harness_path}")
+    else:
+      harness_path = compile_harness(repo_root, args.build_dir.resolve())
+    report = evaluate_suite(harness_path, suite_dir.resolve(), args.timeout)
+
+    if args.output is not None:
+      args.output.parent.mkdir(parents=True, exist_ok=True)
+      args.output.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
+
+    print(json.dumps(report["summary"], indent=2))
+    if args.output is not None:
+      print(args.output.resolve())
+
+    summary = report["summary"]
+    if summary["failed"] > 0 or summary["timeouts"] > 0:
+      return 1
+    return 0
+  finally:
+    if temp_checkout is not None:
+      temp_checkout.cleanup()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/run_toml_test_suite.py` around lines 350 - 369, The temp checkout
created by ensure_suite_checkout may not be cleaned up if an exception occurs in
compile_harness, evaluate_suite, or output writing; wrap the workflow that uses
temp_checkout (the calls to compile_harness, evaluate_suite, writing
args.output, and printing) in a try/finally so that temp_checkout.cleanup() is
always called in the finally block (only if temp_checkout is not None), keeping
the same logic for harness_path and report variables; reference
ensure_suite_checkout, compile_harness, evaluate_suite, args.output, and
temp_checkout.cleanup when locating where to add the try/finally.
docs/testing.md (1)

245-252: Clarify which toml-test checkout --suite-dir should target.

Small doc hardening: add one sentence that --suite-dir should point to a toml-test checkout from main (the tree containing tests/files-toml-1.1.0) to avoid accidental reduced coverage from older/tag-only checkouts.

📝 Suggested wording
 For TOML 1.1.0 checks against the official `toml-test` corpus, run:
@@
 python3 scripts/run_toml_test_suite.py --suite-dir=/path/to/toml-test
 python3 scripts/run_toml_test_suite.py --harness=./build/GocciaTOMLCheck --output=tmp/toml-suite-results.json

+When using --suite-dir, point it to a toml-test checkout from main (the one containing tests/files-toml-1.1.0) to run the full corpus.

</details>

Based on learnings: `scripts/run_toml_test_suite.py` intentionally uses `toml-test` `main`; the `v1.1.0` tag exposes fewer cases and omits `tests/files-toml-1.1.0`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/testing.md around lines 245 - 252, Add a clarifying sentence to the
TOML test invocation docs so users know the --suite-dir should point to a
toml-test checkout from main (the branch that contains tests/files-toml-1.1.0)
rather than a v1.1.0 tag-only checkout; update the example block in
docs/testing.md alongside the python3 scripts/run_toml_test_suite.py usage to
include this sentence and use the suggested wording (or equivalent) referencing
the tests/files-toml-1.1.0 path, and ensure the guidance mentions that
scripts/run_toml_test_suite.py expects the full main corpus for complete
coverage.


</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @docs/testing.md:

  • Around line 245-252: Add a clarifying sentence to the TOML test invocation
    docs so users know the --suite-dir should point to a toml-test checkout from
    main (the branch that contains tests/files-toml-1.1.0) rather than a v1.1.0
    tag-only checkout; update the example block in docs/testing.md alongside the
    python3 scripts/run_toml_test_suite.py usage to include this sentence and use
    the suggested wording (or equivalent) referencing the tests/files-toml-1.1.0
    path, and ensure the guidance mentions that scripts/run_toml_test_suite.py
    expects the full main corpus for complete coverage.

In @scripts/run_toml_test_suite.py:

  • Around line 350-369: The temp checkout created by ensure_suite_checkout may
    not be cleaned up if an exception occurs in compile_harness, evaluate_suite, or
    output writing; wrap the workflow that uses temp_checkout (the calls to
    compile_harness, evaluate_suite, writing args.output, and printing) in a
    try/finally so that temp_checkout.cleanup() is always called in the finally
    block (only if temp_checkout is not None), keeping the same logic for
    harness_path and report variables; reference ensure_suite_checkout,
    compile_harness, evaluate_suite, args.output, and temp_checkout.cleanup when
    locating where to add the try/finally.

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `c27cdbb3-88f0-4dca-b33f-36b8352fcc0d`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7c99662c4535faedf3c043211635348d40fcfa3e and 3f348031b72ad227233c54e98df1546f8e4f4824.

</details>

<details>
<summary>📒 Files selected for processing (11)</summary>

* `README.md`
* `docs/built-ins.md`
* `docs/design-decisions.md`
* `docs/language-restrictions.md`
* `docs/testing.md`
* `scripts/run_toml_test_suite.py`
* `units/Goccia.Engine.pas`
* `units/Goccia.FileExtensions.pas`
* `units/Goccia.Modules.Loader.pas`
* `units/Goccia.Runtime.Bootstrap.pas`
* `units/Goccia.ScriptLoader.Globals.pas`

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (2)</summary>

* README.md
* docs/language-restrictions.md

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (3)</summary>

* units/Goccia.Runtime.Bootstrap.pas
* docs/design-decisions.md
* units/Goccia.FileExtensions.pas

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@frostney frostney merged commit 73cff5c into main Apr 5, 2026
9 checks passed
@frostney frostney deleted the feature/toml-support branch April 5, 2026 00:36
@frostney frostney added the new feature New feature or request label Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add TOML support

1 participant