Skip to content

Add unsafe Function constructor support#386

Merged
frostney merged 9 commits intomainfrom
t3code/unsafe-function-constructor
Apr 23, 2026
Merged

Add unsafe Function constructor support#386
frostney merged 9 commits intomainfrom
t3code/unsafe-function-constructor

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 22, 2026

Summary

  • Adds a new --unsafe-function-constructor engine option and matching goccia.json config key.
  • Replaces the built-in Function constructor with a dedicated implementation that parses parameter and body strings into callable functions.
  • Keeps the feature disabled by default and raises a TypeError unless explicitly enabled.
  • Adds coverage for basic construction, error cases, scope behavior, and disabled-mode behavior.

- Gate dynamic Function construction behind a new CLI/config flag
- Implement Function constructor creation and add coverage tests
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

Adds opt-in dynamic Function constructor support: new unsafe-function-constructor CLI/config flag; engine exposes FunctionConstructor with an Enabled gate and CompileDynamicFunction/ExecuteDynamicFunction paths; implements Function constructor class and adds tests for behavior, errors, properties, scope, and disabled mode.

Changes

Cohort / File(s) Summary
CLI & Application
source/shared/CLI.Options.pas, source/app/Goccia.CLI.Application.pas
Add unsafe-function-constructor flag and property; include it in returned options array; apply resolved flag/config precedence to set Engine.FunctionConstructor.Enabled during startup.
Engine Core & Execution API
source/units/Goccia.Engine.pas, source/units/Goccia.Executor.pas, source/units/Goccia.Engine.Backend.pas
Expose FunctionConstructor property and add CompileDynamicFunction; introduce abstract ExecuteDynamicFunction on executor and implement it in bytecode executor to compile/execute dynamic-function programs and preserve modules for closures.
Function Constructor Value
source/units/Goccia.Values.ClassValue.pas
Add TGocciaFunctionConstructorClassValue with Enabled and CompileDynamicFunction callback; implement BuildFunction/CALL/Create behavior, argument-to-source mapping, and runtime errors when disabled or missing compiler.
Runtime Bootstrap
source/units/Goccia.Runtime.Bootstrap.pas
Instantiate global Function as TGocciaFunctionConstructorClassValue (instead of generic class value) while retaining prototype wiring.
Tests & Test Config
tests/built-ins/Function/constructor/*, tests/built-ins/Function/constructor/goccia.json
Add tests for constructor basic behavior, errors, properties, scope, and disabled mode; add per-test config enabling unsafe-function-constructor.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI
    participant App as Application
    participant Engine as Engine
    participant FC as FunctionConstructor
    participant Compiler as Parser/Compiler
    participant Exec as Executor

    CLI->>App: parse flags & per-file/root config
    App->>Engine: apply resolved unsafe-function-constructor flag
    Engine->>FC: set Enabled and assign CompileDynamicFunction delegate
    FC->>Engine: BuildFunction(request args → param/ body sources)
    Engine->>Compiler: parse/validate param/body → TGocciaProgram
    Compiler-->>Engine: return TGocciaProgram
    Engine->>Exec: ExecuteDynamicFunction(TGocciaProgram)
    Exec-->>Engine: return TGocciaValue (function) — module retained for closures
    Engine-->>FC: return TGocciaFunctionBase or raise SyntaxError/TypeError
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add unsafe Function constructor support' accurately and concisely describes the main change: introducing a new Function constructor feature controlled by the unsafe-function-constructor flag.
Description check ✅ Passed The description covers the main changes (new option, Function constructor replacement, disabled-by-default behavior, test coverage) but does not include the testing checklist items from the template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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

@coderabbitai coderabbitai Bot added new feature New feature or request internal Refactoring, CI, tooling, cleanup labels Apr 22, 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: 2

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

Inline comments:
In `@tests/built-ins/Function/constructor-disabled.js`:
- Around line 7-13: Update the two assertions in the tests titled "new
Function() throws TypeError when disabled" and "Function() throws TypeError when
disabled" to assert the specific exception class instead of any error; change
expect(() => new Function("return 1")).toThrow() to expect(() => new
Function("return 1")).toThrow(TypeError) and likewise change expect(() =>
Function("return 1")).toThrow() to expect(() => Function("return
1")).toThrow(TypeError) so the tests fail if a different error type is thrown.

In `@tests/built-ins/Function/constructor/errors.js`:
- Around line 7-13: Tests that construct new Function with invalid source use
bare toThrow(), which may allow wrong error types; update the two assertions
that call expect(() => new Function("}{")) and expect(() => new Function("@bad",
"return 1")) to assert the specific error class by using toThrow(SyntaxError)
(or toThrowError(SyntaxError)) so the test explicitly requires a SyntaxError for
parse failures in the Function constructor.
🪄 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: 46807403-1a48-483e-ab19-e3fd56b60d11

📥 Commits

Reviewing files that changed from the base of the PR and between 99fba02 and 58b9d48.

📒 Files selected for processing (11)
  • source/app/Goccia.CLI.Application.pas
  • source/shared/CLI.Options.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Runtime.Bootstrap.pas
  • source/units/Goccia.Values.ClassValue.pas
  • tests/built-ins/Function/constructor-disabled.js
  • tests/built-ins/Function/constructor/basic.js
  • tests/built-ins/Function/constructor/errors.js
  • tests/built-ins/Function/constructor/goccia.json
  • tests/built-ins/Function/constructor/properties.js
  • tests/built-ins/Function/constructor/scope.js

Comment thread tests/built-ins/Function/constructor-disabled.js
Comment thread tests/built-ins/Function/constructor/errors.js
Use toThrow(TypeError) and toThrow(SyntaxError) instead of bare
toThrow() so wrong exception types don't silently pass.

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

github-actions Bot commented Apr 22, 2026

Benchmark Results

386 benchmarks

Interpreted: 🟢 218 improved · 🔴 7 regressed · 161 unchanged · avg +3.2%
Bytecode: 🟢 20 improved · 🔴 311 regressed · 55 unchanged · avg -6.0%

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

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 22, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 7706 7706
Tests Passed 7665 ✅ 7706 ✅
Tests Skipped 41 0
Tests Workers 4 4
Tests Test Duration 1.50s 1.50s
Tests Lex (cumulative) 190.8ms 119.7ms
Tests Parse (cumulative) 277.0ms 274.5ms
Tests Compile (cumulative) 170.4ms
Tests Execute (cumulative) 1.55s 1.56s
Tests Engine Total (cumulative) 2.02s 2.12s
Tests Lex (avg/worker) 47.7ms 29.9ms
Tests Parse (avg/worker) 69.3ms 68.6ms
Tests Compile (avg/worker) 42.6ms
Tests Execute (avg/worker) 388.5ms 389.8ms
Tests Engine Total (avg/worker) 505.5ms 531.0ms
Benchmarks Total 386 386
Benchmarks Workers 4 4
Benchmarks Duration 3.02min 2.81min

Measured on ubuntu-latest x64.

- Route Function constructor through executor-level dynamic compilation
- Preserve anonymous naming and this-binding semantics for generated functions
- Remove the old global-scope-backed Function constructor path
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 `@source/units/Goccia.Engine.pas`:
- Around line 1533-1537: The parser created for dynamic functions
(TGocciaParser.Create) does not inherit the engine compatibility flags (cfASI,
cfVar), causing Function(...) bodies to be parsed more strictly than top-level
code; fix by copying the engine's compatibility flags into the parser instance
(i.e., set the parser's CompatibilityFlags or equivalent to include cfASI and
cfVar from the engine/host before calling Parser.ParseUnchecked) so the parser
used in the dynamic-function path honors the same flags as the main engine.

In `@source/units/Goccia.Values.ClassValue.pas`:
- Around line 1244-1271: BuildFunction currently calls the compiler callback
FCompileDynamicFunction unguarded (after checking FEnabled) which can AV if the
bootstrap forgot to wire it; modify BuildFunction to check
Assigned(FCompileDynamicFunction) before calling it and, if nil, raise a JS
error (e.g., via ThrowTypeError or an appropriate engine error) with a clear
message like "Dynamic function compiler not initialized" so callers get a
JS-level exception instead of a crash.
🪄 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: 2a7d6bb4-7063-4129-afcd-1a08eaf09afa

📥 Commits

Reviewing files that changed from the base of the PR and between 25e45fc and 294930c.

📒 Files selected for processing (5)
  • source/units/Goccia.Engine.Backend.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Executor.pas
  • source/units/Goccia.Runtime.Bootstrap.pas
  • source/units/Goccia.Values.ClassValue.pas

Comment thread source/units/Goccia.Engine.Backend.pas
Comment thread source/units/Goccia.Engine.pas
Comment thread source/units/Goccia.Values.ClassValue.pas Outdated
Resolve conflict in ApplyFileConfigToEngine: adopt main's
ResolveFlagOption helper for the unsafe-function-constructor
flag, matching the ASI and compat-var precedence pattern.

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
source/units/Goccia.Values.ClassValue.pas (1)

1278-1315: ⚠️ Potential issue | 🟠 Major

Guard FCompileDynamicFunction before invocation to prevent runtime AV.

FCompileDynamicFunction is initialized to nil at Line 1278 and called unconditionally at Line 1314. If wiring is missed in any bootstrap path, this crashes instead of throwing a JS error.

Proposed fix
 function TGocciaFunctionConstructorClassValue.BuildFunction(
   const AArguments: TGocciaArgumentsCollection): TGocciaFunctionBase;
@@
   if not FEnabled then
     ThrowTypeError('Dynamic code generation is disabled. ' +
       'Pass --unsafe-function-constructor to enable the Function constructor');
+  if not Assigned(FCompileDynamicFunction) then
+    ThrowTypeError('Function constructor is not available in this runtime');

@@
   Result := FCompileDynamicFunction(Source);
 end;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Values.ClassValue.pas` around lines 1278 - 1315,
BuildFunction currently calls FCompileDynamicFunction unconditionally which can
be nil; add a guard in TGocciaFunctionConstructorClassValue.BuildFunction before
calling FCompileDynamicFunction to detect nil and raise a JS-level error (use
ThrowTypeError or the existing runtime error helper) explaining the Function
constructor is unavailable or not wired, then only call
FCompileDynamicFunction(Source) when it's not nil; reference
FCompileDynamicFunction and TGocciaFunctionConstructorClassValue.BuildFunction
to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@source/units/Goccia.Values.ClassValue.pas`:
- Around line 1278-1315: BuildFunction currently calls FCompileDynamicFunction
unconditionally which can be nil; add a guard in
TGocciaFunctionConstructorClassValue.BuildFunction before calling
FCompileDynamicFunction to detect nil and raise a JS-level error (use
ThrowTypeError or the existing runtime error helper) explaining the Function
constructor is unavailable or not wired, then only call
FCompileDynamicFunction(Source) when it's not nil; reference
FCompileDynamicFunction and TGocciaFunctionConstructorClassValue.BuildFunction
to locate the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2a9f4189-eaba-4c0c-a165-250a4efae106

📥 Commits

Reviewing files that changed from the base of the PR and between 294930c and 7e5bc02.

📒 Files selected for processing (3)
  • source/app/Goccia.CLI.Application.pas
  • source/shared/CLI.Options.pas
  • source/units/Goccia.Values.ClassValue.pas

frostney and others added 2 commits April 23, 2026 11:26
Add a nil check before calling FCompileDynamicFunction in
BuildFunction. If the callback was never wired (e.g. via
TGocciaRuntimeBootstrap), this now throws a clear TypeError
instead of crashing with an access violation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The parser created by CompileDynamicFunction now inherits
cfASI and cfVar from the engine's compatibility flags, so
Function(...) bodies are parsed with the same rules as
top-level code.

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

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

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

Inline comments:
In `@source/units/Goccia.Values.ClassValue.pas`:
- Around line 1313-1317: The current code builds Source by interpolating
ParamStr and BodyStr into a wrapper string and calls
FCompileDynamicFunction(Source), which allows crafted inputs to escape the
wrapper; instead add a dedicated parsing path that accepts params+body directly
(e.g., change FCompileDynamicFunction to accept ParamStr and BodyStr or add a
new FCompileDynamicFunctionFromParts), update the call site that currently sets
Source (the '({ anonymous(' + ParamStr + ') {' + BodyStr + '}}).anonymous'
construction) to call the new parser entry, and implement the parser change in
TGocciaEngine so it parses a dynamic function node from the two pieces rather
than by embedding them in a synthetic program string; ensure downstream callers
(TGocciaEngine.CompileDynamicFunction) use the new safe entry point.
🪄 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: 40a24f22-a8d0-45a8-993d-0b8f97c6740e

📥 Commits

Reviewing files that changed from the base of the PR and between 7e5bc02 and fe2f259.

📒 Files selected for processing (2)
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Values.ClassValue.pas

Comment thread source/units/Goccia.Values.ClassValue.pas Outdated
Parse params and body as separate, self-contained programs before
interpolating into the method shorthand wrapper. This prevents
crafted input from escaping the wrapper and executing arbitrary
code during construction.

Params are validated via `(params) => {}` and body via
`() => { body }`. Both must parse as exactly one statement
(the arrow function). If either contains tokens that break
out of its context, the parser rejects it as a SyntaxError
before the wrapper is ever assembled.

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

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

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

Inline comments:
In `@source/units/Goccia.Engine.pas`:
- Around line 1564-1568: ProgramNode validation is too weak and leaks on error:
after Parser.Parse ensure the AST wrapper shape is exactly a single expression
statement whose expression is a TGocciaArrowFunctionExpression (e.g., check
ProgramNode.Body.Count = 1 AND ProgramNode.Body[0] is TGocciaExpressionStatement
AND (ProgramNode.Body[0] as TGocciaExpressionStatement).Expression is
TGocciaArrowFunctionExpression) before calling ParseUnchecked; if validation
fails call ProgramNode.Free first then ThrowSyntaxError to avoid leaking the
temporary AST; apply the same stricter validation and free-before-throw fix to
the other occurrence referenced (around the later block using ThrowSyntaxError).
🪄 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: cf10246b-2317-4810-81de-db2805941bf5

📥 Commits

Reviewing files that changed from the base of the PR and between fe2f259 and c8c6e70.

📒 Files selected for processing (2)
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Values.ClassValue.pas

Comment thread source/units/Goccia.Engine.pas Outdated
frostney and others added 2 commits April 23, 2026 12:50
Tighten the pre-validation shape check: instead of only checking
Body.Count = 1, verify the statement is a TGocciaExpressionStatement
whose expression is a TGocciaArrowFunctionExpression. This rejects
crafted inputs that produce a single non-arrow statement.

Wrap ProgramNode in try/finally so it is freed even when
ThrowSyntaxError raises, preventing an AST leak on invalid input.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve conflicts in Engine.pas and Runtime.Bootstrap.pas:
keep TGocciaFunctionConstructorClassValue while adopting
the new Function.prototype chain setup from main (shared
prototype wiring, SetDefaultPrototype, PatchDefaultPrototype,
FTypedArrayIntrinsic).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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

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

Inline comments:
In `@source/units/Goccia.Engine.pas`:
- Around line 1617-1628: The parser warnings from Parser.Parse are currently
discarded, allowing unsupported syntax to be silently ignored; after calling
Parser.Parse (where ProgramNode is examined and ParseUnchecked used later) you
should surface any parser warnings by invoking the parser's warning reporting
routine (e.g. PrintParserWarnings or equivalent) before freeing
ProgramNode/Parser so that warnings for unsupported syntax in parameter
lists/defaults are emitted; update the blocks around ProgramNode := Parser.Parse
and the corresponding ParseUnchecked usage (references: ProgramNode,
Parser.Parse, ParseUnchecked, PrintParserWarnings, TGocciaExpressionStatement,
TGocciaArrowFunctionExpression, ThrowSyntaxError) to call the warning printer
after validation and before freeing resources, and apply the same change to the
other occurrence noted (lines ~1647-1658).
🪄 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: f27ae757-f6a0-457c-b1b6-53e4de66181d

📥 Commits

Reviewing files that changed from the base of the PR and between c8c6e70 and d3cb116.

📒 Files selected for processing (3)
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Runtime.Bootstrap.pas
  • source/units/Goccia.Values.ClassValue.pas

Comment thread source/units/Goccia.Engine.pas
@frostney frostney merged commit 99aed6c into main Apr 23, 2026
10 checks passed
@frostney frostney deleted the t3code/unsafe-function-constructor branch April 23, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Refactoring, CI, tooling, cleanup new feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant