Skip to content

Implement non-strict arguments and with support#645

Merged
frostney merged 15 commits into
mainfrom
t3code/a72fd50b
May 17, 2026
Merged

Implement non-strict arguments and with support#645
frostney merged 15 commits into
mainfrom
t3code/a72fd50b

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented May 15, 2026

Summary

  • Implement opt-in Script-source arguments objects and with object environments behind --compat-non-strict-mode in interpreter and bytecode mode, while keeping modules and strict directive code on strict semantics.
  • Align strict-mode edge cases added during review: arguments.callee is a throwing accessor, onlyStrict test262 cases receive a strict directive while retaining compat parser support, and module with syntax now fails consistently in interpreter and bytecode paths.
  • Preserve bytecode/interpreter parity for with Symbol.unscopables, closures, assignment/update behavior, method-call receivers, and ASI around with followed by let.
  • Include related conformance fixes surfaced by the PR: strict directive handling, bytecode array max-index length truncation, typed-array integer-indexed [[Set]], non-strict assignment/delete/this semantics, and the bundler repeated-NaN constant crash.
  • Closes Compat flag for non-strict mode semantics (--compat-non-strict-mode) #586.

Testing

  • Verified no regressions and confirmed the new feature or bugfix in end-to-end JavaScript/TypeScript tests
  • Updated documentation
  • Optional: Verified no regressions and confirmed the new feature or bugfix in native Pascal tests (if AST, scope, evaluator, or value types changed)
  • Optional: Verified no benchmark regressions or confirmed benchmark coverage for the change

Verification run:

  • ./format.pas --check
  • git diff --check
  • ./build.pas clean testrunner loader loaderbare bundler benchmarkrunner repl
  • ./build.pas tests (native Pascal test target builds successfully)
  • bun scripts/test-cli.ts
  • ./build/GocciaTestRunner tests/language/arguments --mode=bytecode --no-progress
  • ./build/GocciaTestRunner tests/language/non-strict-mode --no-progress
  • ./build/GocciaTestRunner tests/language/non-strict-mode --mode=bytecode --no-progress
  • ./build/GocciaTestRunner tests/language/non-strict-mode-modules --no-progress
  • ./build/GocciaTestRunner tests/language/non-strict-mode-modules --mode=bytecode --no-progress
  • bun scripts/run_test262_suite.ts --suite-dir /tmp/goccia-test262-suite --filter "language/arguments-object/10.6-14-c-4-s.js" --mode=interpreted --jobs=1 --output=/tmp/t262-args-callee-interpreted.json
  • bun scripts/run_test262_suite.ts --suite-dir /tmp/goccia-test262-suite --filter "language/arguments-object/10.6-14-c-4-s.js" --mode=bytecode --jobs=1 --output=/tmp/t262-args-callee-bytecode.json

Full tests --asi --unsafe-ffi and bytecode full-suite runs before the latest reviewer fixes both reached 9331/9336 passing; the remaining 5 failures were the existing local FFI fixture load issue for ./fixtures/ffi/libfixture.dylib.

- add unmapped arguments objects for interpreter and bytecode functions
- implement with statement object environments across interpreter and VM
- add regression coverage for arguments, with, and repeated NaN bundling
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
gocciascript-homepage Ignored Ignored Preview May 17, 2026 1:56pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: def27f84-4544-4434-96eb-f6171ac2c8a4

📥 Commits

Reviewing files that changed from the base of the PR and between 2b33b7c and d229367.

📒 Files selected for processing (2)
  • source/units/Goccia.Values.ArrayValue.pas
  • tests/built-ins/Object/defineProperty.js

📝 Walkthrough

Walkthrough

Adds --compat-non-strict-mode across CLI/engine; enables optional unmapped arguments and with-statement parsing/execution; extends compiler and bytecode to emit with/arguments/loose ops; updates VM call-frame arguments, evaluator/scope non-strict semantics, typed-array behavior, test262 runner, tests, and docs.

Changes

Compatibility non-strict mode & runtime/compiler/VM updates

Layer / File(s) Summary
CLI & Engine wiring
source/shared/*, source/app/*
Adds compat-non-strict-mode option, TGocciaEngine.NonStrictModeEnabled, and threads flag into interpreter, bytecode executor, module loader, REPL, bundler, benchmark runner, and per-file config application.
Parser & AST (with / directives)
source/units/Goccia.Parser.pas, source/units/Goccia.AST.Statements.pas
Adds Parser.NonStrictModeEnabled, conditional with parsing, ASI/let disambiguation under compat mode, directive-prologue helpers, and new TGocciaWithStatement AST node and runtime Execute behaviour.
Compiler: with-bindings & arguments lowering
source/units/Goccia.Compiler.*, source/units/Goccia.AST.BindingPatterns.pas
Compiler scope tracks hidden with-bindings; emits OP_HAS_WITH_BINDING probes; implements with-aware identifier access/assignment/calls; adds DeclareArgumentsObjectLocal/EmitCreateArgumentsObject and propagates child strictness.
Compiler statements & templates
source/units/Goccia.Compiler.Statements.pas, source/units/Goccia.Compiler.pas
CompileWithStatement implementation, hoisting support, method/getter/setter/field/static block argument-object integration, and template strictness computation.
Engine & bytecode templates
source/units/Goccia.Engine.pas, source/units/Goccia.Bytecode.Chunk.pas, source/units/Goccia.Bytecode.Binary.pas
Adds cfNonStrictMode, engine getters/setters, function template StrictThis/StrictCode fields, bytecode format bump and writer/reader update to include strict flags.
VM & bytecode opcodes
source/units/Goccia.VM.pas, source/units/Goccia.Bytecode.pas, source/units/Goccia.Bytecode.OpCodeNames.pas
Adds FCurrentArguments and per-frame Arguments, OP_CREATE_ARGUMENTS/OP_PACK_ARGS/OP_TO_OBJECT/OP_HAS_WITH_BINDING, loose set/delete/index/global opcodes and handlers, HasWithBindingValue helper, and GC marking for argument registers.
Evaluator & Scope changes
source/units/Goccia.Evaluator.pas, source/units/Goccia.Scope.pas, source/units/Goccia.Interpreter.pas
Scope adds NonStrictMode and ResolveIdentifierReference/DeleteBinding changes; evaluator uses ANonStrictMode for assignment/delete paths, resolves identifier-call this; interpreter threads NonStrictMode into contexts and respects use-strict directives.
Assignment helpers & Proxy/Object tweaks
source/units/Goccia.Evaluator.Assignment.pas, source/units/Goccia.Values.ProxyValue.pas, source/units/Goccia.Values.ObjectValue.pas
AssignProperty/AssignSymbolProperty gain ANonStrictMode behavior; compound-assignment helpers return values; Proxy fallback uses AssignPropertyWithReceiver; prototype lookup uses GetOwnPropertyDescriptor.
Arguments object & function execution
source/units/Goccia.Values.ArgumentsObjectValue.pas, source/units/Goccia.Values.FunctionValue.pas, source/units/Goccia.Values.GeneratorValue.pas
Implements CreateUnmappedArgumentsObject, conditional binding of arguments for functions/generators/methods when compat enabled, Adds CreatesArgumentsObject hook and arrow override.
TypedArray & ArrayValue improvements
source/units/Goccia.Values.TypedArrayValue.pas, source/units/Goccia.Values.ArrayValue.pas
Buffer sync/GetLength, canonical integer-index handling, BigInt conversion helpers, receiver-aware integer-index sets, deletion of sparse indexes on length truncation, and array length truncation helper.
Constant folding & float dedupe
source/units/Goccia.Compiler.ConstantFolding.pas, source/units/Goccia.Bytecode.Chunk.pas
TryEvaluateIdentifier early-guards when with-bindings present; AddConstantFloat NaN-ness precomputation and matching refinements.
Scripts, test262 runner, tests & docs
scripts/*, tests/*, docs/*
Test262 runner injects "use strict" for onlyStrict tests and conditionally adds --compat-non-strict-mode for non-modules; many new/updated tests for with/arguments/non-strict/typedarray; CLI end-to-end tests; updated docs and goccia.json examples.

Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Engine
  participant Parser
  participant Compiler
  participant VM
  CLI->>Engine: set NonStrictModeEnabled
  Engine->>Parser: NonStrictModeEnabled
  Parser->>Compiler: emit with/arguments/loose ops
  Compiler->>VM: bytecode (OP_CREATE_ARGUMENTS / OP_HAS_WITH_BINDING / loose ops)
  VM->>Runtime: execute with/assign/delete semantics
Loading

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

Benchmark Results

407 benchmarks

Interpreted: 🟢 36 improved · 🔴 108 regressed · 263 unchanged · avg -3.4%
Bytecode: 🟢 29 improved · 🔴 198 regressed · 180 unchanged · avg -6.3%

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

Deterministic profile diff

Deterministic profile diff: no significant changes.

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

Suite Timing

Test Runner (interpreted: 9,467 passed; bytecode: 9,467 passed)
Metric Interpreted Bytecode
Total 9467 9467
Passed 9467 ✅ 9467 ✅
Workers 4 4
Test Duration 2.20s 2.30s
Lex (cumulative) 261.7ms 179.4ms
Parse (cumulative) 280.5ms 293.8ms
Compile (cumulative) 591.8ms
Execute (cumulative) 2.60s 2.34s
Engine Total (cumulative) 3.15s 3.41s
Lex (avg/worker) 65.4ms 44.9ms
Parse (avg/worker) 70.1ms 73.4ms
Compile (avg/worker) 148.0ms
Execute (avg/worker) 651.0ms 585.2ms
Engine Total (avg/worker) 786.6ms 851.5ms

Memory

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

Metric Interpreted Bytecode
GC Live 251.65 MiB 245.91 MiB
GC Peak Live 251.65 MiB 245.92 MiB
GC Allocated During Run 255.64 MiB 249.91 MiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 1 1
GC Collected Objects 88 88
Heap Start Allocated 153.1 KiB 153.1 KiB
Heap End Allocated 1.46 MiB 1.46 MiB
Heap Delta Allocated 1.31 MiB 1.31 MiB
Heap Delta Free 608.0 KiB 608.0 KiB
Benchmarks (interpreted: 407; bytecode: 407)
Metric Interpreted Bytecode
Total 407 407
Workers 4 4
Duration 2.47min 2.33min

Memory

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

Metric Interpreted Bytecode
GC Live 3.64 MiB 3.64 MiB
GC Peak Live 95.57 MiB 73.62 MiB
GC Allocated During Run 13.85 GiB 9.09 GiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 2,817 2,670
GC Collected Objects 262,836,046 215,745,735
Heap Start Allocated 1.21 MiB 1.21 MiB
Heap End Allocated 1.21 MiB 1.21 MiB
Heap Delta Allocated 128 B 128 B

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

test262 Conformance

🚫 Regression vs cached main baseline. 63 previously-passing test(s) now fail; pass count Δ +1203. This run blocks merge — see "Newly failing" below.

Category Run Passed Δ Pass Failed Pass-rate Δ Rate
built-ins 23,449 15,592 +600 7,851 66.5% +2.6pp
harness 116 71 ±0 45 61.2% ±0pp
intl402 3,324 741 ±0 2,583 22.3% ±0pp
language 23,635 13,218 +567 10,417 55.9% +2.4pp
staging 1,484 541 +36 941 36.5% +2.4pp
total 52,008 30,163 +1,203 21,837 58.0% +2.3pp

Areas closest to 100%

Area Pass rate Δ vs main Passing
built-ins/WeakMap 99.3% +0.7pp 140 / 141
built-ins/WeakSet 98.8% ±0pp 84 / 85
language/asi 97.1% ±0pp 99 / 102
Per-test deltas (+1266 / -63)

Newly failing (63):

  • built-ins/Array/prototype/indexOf/15.4.4.14-9-b-i-6.js
  • built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-b-i-6.js
  • built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/find/BigInt/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/find/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/findIndex/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/findLast/BigInt/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/findLast/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/findLastIndex/BigInt/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/findLastIndex/predicate-may-detach-buffer.js
  • built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer.js
  • built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js
  • built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer-throws.js
  • built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/key-is-not-numeric-index-throws.js
  • built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer-throws.js
  • built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-not-numeric-index-throws.js
  • language/eval-code/direct/global-env-rec-with.js
  • language/expressions/class/accessor-name-static-computed-in.js
  • language/expressions/class/accessor-name-static/computed.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.1.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.10.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.11.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.2.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.3.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.4.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.5.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.6.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.7.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.8.js
  • language/expressions/compound-assignment/S11.13.2_A2.1_T3.9.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-expression-import-attributes-trailing-comma-first.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-expression-import-attributes-trailing-comma-second.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-expression-import-defer-empty-str-is-valid-assign-expr.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-expression-import-defer-script-code-valid.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-expression-import-source-empty-str-is-valid-assign-expr.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-expression-import-source-script-code-valid.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-import-attributes-trailing-comma-first.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-import-attributes-trailing-comma-second.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-import-defer-empty-str-is-valid-assign-expr.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-import-defer-script-code-valid.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-import-source-empty-str-is-valid-assign-expr.js
  • language/expressions/dynamic-import/syntax/valid/nested-with-import-source-script-code-valid.js
  • language/expressions/object/11.1.5_3-3-1.js
  • language/expressions/object/11.1.5_4-5-1.js
  • language/expressions/postfix-decrement/S11.3.2_A2.1_T2.js
  • language/expressions/postfix-increment/S11.3.1_A2.1_T2.js
  • language/expressions/prefix-decrement/S11.4.5_A2.1_T2.js
  • language/expressions/prefix-increment/S11.4.4_A2.1_T2.js
  • language/statements/class/accessor-name-static/computed.js
  • language/statements/class/definition/setters-restricted-ids.js
  • staging/sm/expressions/optional-chain-tdz.js
  • staging/sm/extensions/TypedArray-set-object-funky-length-detaches.js

Newly passing (1266):

  • built-ins/Array/15.4.5.1-5-1.js
  • built-ins/Array/from/calling-from-valid-1-noStrict.js
  • built-ins/Array/from/calling-from-valid-1-onlyStrict.js
  • built-ins/Array/from/calling-from-valid-2.js
  • built-ins/Array/from/iter-map-fn-args.js
  • built-ins/Array/isArray/15.4.3.2-1-13.js
  • built-ins/Array/prototype/at/coerced-index-resize.js
  • built-ins/Array/prototype/concat/Array.prototype.concat_holey-sloppy-arguments.js
  • built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js
  • built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments-throws.js
  • built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments-with-dupes.js
  • built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments.js
  • built-ins/Array/prototype/concat/Array.prototype.concat_strict-arguments.js
  • built-ins/Array/prototype/every/15.4.4.16-2-17.js
  • built-ins/Array/prototype/every/15.4.4.16-5-1.js
  • built-ins/Array/prototype/every/15.4.4.16-5-19.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-i-25.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-i-26.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-i-27.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-ii-11.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-ii-13.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-ii-2.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-ii-6.js
  • built-ins/Array/prototype/every/15.4.4.16-7-c-iii-25.js
  • built-ins/Array/prototype/every/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/filter/15.4.4.20-2-17.js
  • built-ins/Array/prototype/filter/15.4.4.20-5-1.js
  • built-ins/Array/prototype/filter/15.4.4.20-5-19.js
  • built-ins/Array/prototype/filter/15.4.4.20-5-30.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-i-25.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-i-26.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-i-27.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-ii-11.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-ii-13.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-ii-2.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-ii-6.js
  • built-ins/Array/prototype/filter/15.4.4.20-9-c-iii-26.js
  • built-ins/Array/prototype/filter/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/find/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/find/predicate-call-parameters.js
  • built-ins/Array/prototype/find/predicate-call-this-non-strict.js
  • built-ins/Array/prototype/findIndex/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/findIndex/predicate-call-parameters.js
  • built-ins/Array/prototype/findIndex/predicate-call-this-non-strict.js
  • built-ins/Array/prototype/findIndex/resizable-buffer.js
  • built-ins/Array/prototype/findLast/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/findLast/predicate-call-parameters.js
  • built-ins/Array/prototype/findLast/predicate-call-this-non-strict.js
  • built-ins/Array/prototype/findLastIndex/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js
  • built-ins/Array/prototype/findLastIndex/predicate-call-this-non-strict.js
  • built-ins/Array/prototype/findLastIndex/resizable-buffer.js
  • built-ins/Array/prototype/flat/array-like-objects.js
  • built-ins/Array/prototype/forEach/15.4.4.18-2-17.js
  • built-ins/Array/prototype/forEach/15.4.4.18-5-1.js
  • built-ins/Array/prototype/forEach/15.4.4.18-5-19.js
  • built-ins/Array/prototype/forEach/15.4.4.18-5-25.js
  • built-ins/Array/prototype/forEach/15.4.4.18-7-c-i-25.js
  • built-ins/Array/prototype/forEach/15.4.4.18-7-c-i-26.js
  • built-ins/Array/prototype/forEach/15.4.4.18-7-c-i-27.js
  • built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-11.js
  • built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-13.js
  • built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-2.js
  • built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-6.js
  • built-ins/Array/prototype/forEach/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/includes/coerced-searchelement-fromindex-resize.js
  • built-ins/Array/prototype/includes/resizable-buffer.js
  • built-ins/Array/prototype/indexOf/15.4.4.14-1-15.js
  • built-ins/Array/prototype/indexOf/15.4.4.14-2-17.js
  • built-ins/Array/prototype/indexOf/15.4.4.14-9-b-i-25.js
  • built-ins/Array/prototype/indexOf/15.4.4.14-9-b-i-26.js
  • built-ins/Array/prototype/indexOf/15.4.4.14-9-b-i-27.js
  • built-ins/Array/prototype/indexOf/coerced-searchelement-fromindex-shrink.js
  • built-ins/Array/prototype/indexOf/resizable-buffer.js
  • built-ins/Array/prototype/join/coerced-separator-shrink.js
  • built-ins/Array/prototype/join/resizable-buffer.js
  • built-ins/Array/prototype/keys/resizable-buffer-grow-mid-iteration.js
  • built-ins/Array/prototype/lastIndexOf/15.4.4.15-1-15.js
  • built-ins/Array/prototype/lastIndexOf/15.4.4.15-2-17.js
  • built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-b-i-25.js
  • built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-b-i-26.js
  • built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-b-i-27.js
  • built-ins/Array/prototype/lastIndexOf/coerced-position-shrink.js
  • built-ins/Array/prototype/lastIndexOf/resizable-buffer.js
  • built-ins/Array/prototype/map/15.4.4.19-2-17.js
  • built-ins/Array/prototype/map/15.4.4.19-5-1.js
  • built-ins/Array/prototype/map/15.4.4.19-5-19.js
  • built-ins/Array/prototype/map/15.4.4.19-8-c-i-25.js
  • built-ins/Array/prototype/map/15.4.4.19-8-c-i-26.js
  • built-ins/Array/prototype/map/15.4.4.19-8-c-i-27.js
  • built-ins/Array/prototype/map/15.4.4.19-8-c-ii-11.js
  • built-ins/Array/prototype/map/15.4.4.19-8-c-ii-13.js
  • built-ins/Array/prototype/map/15.4.4.19-8-c-ii-2.js
  • built-ins/Array/prototype/map/15.4.4.19-8-c-ii-6.js
  • built-ins/Array/prototype/map/callbackfn-resize-arraybuffer.js
  • built-ins/Array/prototype/reduce/15.4.4.21-2-17.js
  • built-ins/Array/prototype/reduce/15.4.4.21-8-b-iii-1-25.js
  • built-ins/Array/prototype/reduce/15.4.4.21-8-b-iii-1-26.js
  • built-ins/Array/prototype/reduce/15.4.4.21-8-b-iii-1-27.js
  • built-ins/Array/prototype/reduce/15.4.4.21-9-c-i-25.js
  • … 1166 more

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

frostney added 11 commits May 15, 2026 11:04
- expand language docs for arguments shadowing and with receiver behavior
- add interpreter and bytecode implementation notes
- link the decision log entry to PR #645
- clarify arguments object coverage and arrow lexical behavior
- document with statement lookup, unscopables, closures, and receiver behavior
- note generator arguments object support in the quick-reference table
- Inject strict directives for onlyStrict test262 script tests while keeping non-strict compat parsing enabled.
- Keep bytecode arguments-object creation tied to compat availability instead of effective sloppy mode.
- Make unmapped arguments.callee a throwing accessor and keep module with strict in all frontends.
@frostney frostney changed the title Implement arguments object and with support Implement non-strict arguments and with support May 17, 2026
@frostney frostney marked this pull request as ready for review May 17, 2026 12:17
@coderabbitai coderabbitai Bot added new feature New feature or request spec compliance Mismatch against official JavaScript/TypeScript specification internal Refactoring, CI, tooling, cleanup labels May 17, 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: 18

Caution

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

⚠️ Outside diff range comments (2)
source/units/Goccia.Compiler.Expressions.pas (1)

1243-1308: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Destructuring assignment identifiers never target with bindings.

EmitDestructuring sends identifier patterns to EmitBindingAssignmentFromRegister, but that helper only checks locals, upvalues, and globals. In assignment mode it never consults ShouldTryWithBinding/EmitWithAssignmentOrFallback, so with (o) ({x} = src) will write the fallback binding instead of o.x.

Also applies to: 1326-1331

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

In `@source/units/Goccia.Compiler.Expressions.pas` around lines 1243 - 1308, The
helper EmitBindingAssignmentFromRegister fails to target "with" bindings for
identifier patterns during assignments; before resolving locals/upvalues, add a
branch that when AAssignmentMode is true and ShouldTryWithBinding(ACtx, AName)
(or the existing predicate used elsewhere) returns true, call
EmitWithAssignmentOrFallback(ACtx, AName, AValueReg) (or the existing helper
signature) and Exit so the assignment writes the with-bound property; make the
same change at the other similar site referenced by EmitDestructuring to ensure
destructuring identifiers use with-bindings in assignment mode.
source/units/Goccia.Parser.pas (1)

3299-3328: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope the let newline fallback to the with ambiguity only.

This rewrites every non-strict let followed by a later-line { into an identifier expression statement. This will incorrectly parse valid sloppy lexical declarations like let\n{a} = obj; as two statements instead of one declaration, since ECMAScript's grammar forbids line terminators between let and the binding pattern. Gate this logic behind the specific with-body disambiguation instead of applying it to all statement contexts.

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

In `@source/units/Goccia.Parser.pas` around lines 3299 - 3328, The current branch
that treats a non-strict `let` followed by a later-line `{` as an identifier
statement (the Check(gttLet) + FNonStrictModeEnabled +
FAutomaticSemicolonInsertion + (FTokens[FCurrent + 1].TokenType = gttLeftBrace)
+ (Peek.Line < FTokens[FCurrent + 1].Line) block) is too broad and breaks valid
sloppy `let` lexical declarations; restrict this behavior so it runs only when
resolving the specific `with`-body ambiguity. Replace the broad condition with
one that also checks a dedicated disambiguation predicate (e.g. call or add
IsWithBodyAmbiguity/ShouldTreatLetAsWithBody at the same spot) and only create
the TGocciaExpressionStatement/TGocciaIdentifierExpression when that predicate
is true, leaving normal `let` declaration parsing untouched otherwise.
🧹 Nitpick comments (1)
tests/language/non-strict-mode/assignment.js (1)

83-96: ⚡ Quick win

Make global state cleanup exception-safe.

If an assertion fails before cleanup, globalThis.__gocciaNonStrictFixedAssignment can leak into subsequent tests. Wrap setup/assertions in try/finally so cleanup always runs.

Proposed patch
   test("global object identifier writes silently fail for non-writable properties", () => {
-    Object.defineProperty(globalThis, "__gocciaNonStrictFixedAssignment", {
-      value: 1,
-      writable: false,
-      configurable: true,
-    });
-
-    const result = (__gocciaNonStrictFixedAssignment = 2);
-
-    expect(result).toBe(2);
-    expect(globalThis.__gocciaNonStrictFixedAssignment).toBe(1);
-
-    delete globalThis.__gocciaNonStrictFixedAssignment;
+    Object.defineProperty(globalThis, "__gocciaNonStrictFixedAssignment", {
+      value: 1,
+      writable: false,
+      configurable: true,
+    });
+    try {
+      const result = (__gocciaNonStrictFixedAssignment = 2);
+
+      expect(result).toBe(2);
+      expect(globalThis.__gocciaNonStrictFixedAssignment).toBe(1);
+    } finally {
+      delete globalThis.__gocciaNonStrictFixedAssignment;
+    }
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/language/non-strict-mode/assignment.js` around lines 83 - 96, The test
creates a non-writable global property __gocciaNonStrictFixedAssignment on
globalThis but leaves deletion to run only on the success path; make cleanup
exception-safe by wrapping the setup and assertions in a try/finally block and
perform delete globalThis.__gocciaNonStrictFixedAssignment in the finally clause
so the global is removed regardless of test failures; locate the test function
(the test(...) block that defines __gocciaNonStrictFixedAssignment) and modify
it to ensure the delete happens in finally.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/value-system.md`:
- Around line 543-544: The sentence that reads "Standalone calls to any function
type receive `undefined` as `this`" is misleading about arrow functions; update
the wording around that sentence (the paragraph containing "Standalone calls to
any function type" and the nearby reference to "`--compat-non-strict-mode`") to
state that standalone calls to ordinary (non-arrow) functions receive
`undefined` as `this` in strict mode, while arrow functions use lexical `this`
and do not get a call-site `this`; keep the existing note that script-source
`--compat-non-strict-mode` coerces nullish `this` for ordinary functions and
that module source remains strict.

In `@scripts/test-cli.ts`:
- Around line 327-328: The test currently only checks bundleNoFlag.exitCode but
not that the produced artifact exists; after the bundling call that sets
bundleNoFlag (using BUNDLER, src and tmp producing "no-flag.gbc"), add an
explicit file-existence assertion (e.g., fs.existsSync or fs.promises.access)
for join(tmp, "no-flag.gbc") and throw a clear Error if the output file is
missing or not readable so the test fails on a silent no-op compile; keep the
existing exitCode check as well.

In `@source/app/GocciaBundler.dpr`:
- Around line 223-224: Compute the effective source-type first and use it when
resolving EffectiveNonStrictMode so bundler matches the bytecode parse path for
modules; call ResolveFlagOption to get an EffectiveSourceType (e.g.
EffectiveSourceType := ResolveFlagOption(EngineOptions.SourceType, FileConfig,
'source-type')) before computing EffectiveNonStrictMode, then incorporate that
value into the non-strict decision (for example, treat source-type='module' as
forcing strict semantics or skip compat non-strict when
EffectiveSourceType='module') instead of basing EffectiveNonStrictMode solely on
ResolveFlagOption(EngineOptions.CompatNonStrictMode, FileConfig,
'compat-non-strict-mode'); apply the same change to the other non-strict
computations referenced by EffectiveNonStrictMode usages at the other locations.

In `@source/units/Goccia.AST.BindingPatterns.pas`:
- Around line 80-84: The name lookup for destructured parameters is using
TStringList.IndexOf which defaults to case-insensitive comparison; update the
logic around Names (the TStringList created when calling
CollectPatternBindingNames on AParameters[I].Pattern) to use case-sensitive
comparisons (e.g., set Names.CaseSensitive := True after creation) so that the
IndexOf check against AName is exact-case like the other string equality usages;
ensure this change is applied where Names is created/used in the routine that
iterates AParameters and checks Names.IndexOf(AName) (referencing Names,
CollectPatternBindingNames, AParameters[I].Pattern, and AName).

In `@source/units/Goccia.Compiler.Expressions.pas`:
- Around line 1044-1048: The fast path currently compiles AExpr.Value before
resolving the with-binding, which can change behavior if the RHS mutates the
with object; change the order so the with-assignment target is resolved first.
Introduce or call a resolver that performs the same lookup logic used by
EmitWithAssignmentOrFallback (e.g. ResolveWithAssignmentTarget or inline the
lookup used there) using ACtx and AExpr.Name to determine and store the
assignment target/binding before calling ACtx.CompileExpression(AExpr.Value,
ADest); then call EmitWithAssignmentOrFallback (or the assignment emitter) to
perform the write using the previously resolved target and the compiled ADest.
- Around line 417-438: ShouldTryWithBinding currently only avoids 'with' lookup
when a same-scope local hides the name, but it must also avoid probing the
with-object when the identifier is a captured outer binding (an upvalue) so
nested functions prefer the captured variable over with.<Name>. Modify
ShouldTryWithBinding to first detect whether AName is captured as an upvalue for
this scope (or any enclosing function scope represented by AScope) and if so
return False before probing the with binding; keep the existing checks
(AScope.WithBindingCount, KEYWORD_THIS, HiddenWithBindingName) and the
local-resolution logic (ResolveLocal/GetLocal/Local.Depth/GetWithBindingDepth)
but add an early check for captured/upvalue status and exit False when true.

In `@source/units/Goccia.Compiler.pas`:
- Around line 637-638: The assignment to FCurrentTemplate.StrictCode currently
allows FNonStrictMode to clear strictness; change it so top-level <module>
entrypoints remain strict regardless of the compatibility flag: when setting
FCurrentTemplate.StrictCode use a conditional that forces True for the module
entrypoint (i.e. if this template is the top-level module entrypoint — use the
existing marker/flag your codebase uses to detect that) otherwise evaluate (not
FNonStrictMode) or HasUseStrictDirective(AProgram); keep the references to
FCurrentTemplate.StrictCode, FNonStrictMode and HasUseStrictDirective(AProgram)
when implementing this conditional.
- Around line 241-243: TGocciaWithStatement bodies are not traversed by
HoistVarLocals, so var declarations inside a with body aren't hoisted before
earlier statements are compiled; update HoistVarLocals to walk the
TGocciaWithStatement.Body (or add a specific branch handling
TGocciaWithStatement in the traversal) so that any var/local bindings declared
inside the with body are hoisted the same way as other compound statements;
ensure the traversal/visitor used by HoistVarLocals also handles nodes produced
by CompileWithStatement and references TGocciaWithStatement and its Body so
semantics match other statement types.

In `@source/units/Goccia.Compiler.Statements.pas`:
- Around line 1182-1183: The current TGocciaWithStatement branch only inspects
the Body; update it so StatementNeedsIteratorClose also considers the with's
ObjectExpression (and any OP_TO_OBJECT usage) because evaluating the expression
or OP_TO_OBJECT can throw after iterator.next() has yielded a value; i.e., in
the branch handling TGocciaWithStatement call StatementNeedsIteratorClose on
both TGocciaWithStatement(AStmt).Body and
TGocciaWithStatement(AStmt).ObjectExpression (or otherwise treat the with node
as needing iterator-close if its object expression or OP_TO_OBJECT may throw) so
iterator-close analysis remains conservative.

In `@source/units/Goccia.Evaluator.pas`:
- Around line 1033-1041: EvaluateTaggedTemplate currently evaluates a bare
identifier tag with EvaluateExpression and forces ThisValue to
TGocciaUndefinedLiteralValue, losing the receiver in sloppy/with scopes; change
the tag resolution to mirror the call-site logic: if the tag expression is
TGocciaIdentifierExpression, call AContext.Scope.ResolveIdentifierReference with
the identifier name to obtain both the Callee and ThisValue, otherwise fall back
to EvaluateExpression and set ThisValue to undefined (reuse the same
TGocciaIdentifierExpression, ACallExpression.Callee, ThisValue,
EvaluateTaggedTemplate and AContext.Scope.ResolveIdentifierReference symbols to
locate and update the code).

In `@source/units/Goccia.Scope.pas`:
- Around line 698-710: ResolveIdentifierReference currently always leaves
AThisValue undefined except for KEYWORD_THIS; change it to preserve the receiver
when the resolved binding comes from an object (including the global object).
Call GetBinding once into a local binding variable (use the same symbol returned
by GetBinding in ResolveIdentifierReference), assign AValue from binding.Value,
and then set AThisValue from the binding's receiver/owner (or the global object)
instead of leaving it as TGocciaUndefinedLiteralValue; keep the existing
special-case for KEYWORD_THIS that returns FThisValue.

In `@source/units/Goccia.Values.ArgumentsObjectValue.pas`:
- Around line 55-61: ArrayPrototypeValuesFunction currently does a live
prototype lookup by creating a TGocciaArrayValue and calling
GetProperty(PROP_VALUES), which allows later mutations to Array.prototype.values
to affect newly created arguments iterators; instead capture the realm intrinsic
at arguments object creation. Update CreateUnmappedArgumentsObject to fetch and
store the realm's %Array.prototype.values% intrinsic (do not call
ArrayPrototypeValuesFunction/GetProperty(PROP_VALUES) at creation time) so the
arguments object's Symbol.iterator points to the realm-captured function;
reference the realm/intrinsics accessor used elsewhere in the project to obtain
the array prototype values intrinsic and use that stored value when wiring up
the unmapped arguments object's iterator rather than performing a dynamic
GetProperty on TGocciaArrayValue.

In `@source/units/Goccia.Values.ArrayValue.pas`:
- Around line 487-500: The helper DeleteSparseArrayIndexesAtOrAbove currently
discards the Boolean return from AArray.DeleteProperty which hides failures for
non-configurable sparse indexes; change DeleteSparseArrayIndexesAtOrAbove to
return Boolean and, while iterating Keys and invoking AArray.DeleteProperty,
check its return and immediately return False on failure so truncation aborts
atomically; then update the callers (the places that call DefineProperty and
TryDefineProperty which perform length truncation) to check the helper's Boolean
result and propagate/return failure so DefineProperty/TryDefineProperty halt
truncation when DeleteSparseArrayIndexesAtOrAbove fails. Ensure references to
TGocciaArrayValue, FElements behavior, and DeleteProperty semantics are
preserved.

In `@source/units/Goccia.Values.FunctionValue.pas`:
- Around line 205-209: Currently the guard only creates the unmapped arguments
object when the function is not strict, which omits the required arguments
binding for strict-directive functions under CompatibilityNonStrictMode; change
the logic so that when CompatibilityNonStrictMode and CreatesArgumentsObject are
true and neither ParameterListBindsName(FParameters, IDENTIFIER_ARGUMENTS) nor
ACallScope.ContainsOwnLexicalBinding(IDENTIFIER_ARGUMENTS) is true you always
call ACallScope.DefineVariableBinding(IDENTIFIER_ARGUMENTS,
CreateUnmappedArgumentsObject(AArguments), True) to create the unmapped
arguments object, and move any sloppy-only parameter-aliasing behavior behind a
separate condition checking not FStrictCode so FStrictCode no longer prevents
the unmapped binding creation.

In `@source/units/Goccia.Values.TypedArrayValue.pas`:
- Around line 237-259: Several methods still read the internal field FLength
directly causing stale-length bugs for length-tracking arrays; update algorithms
that loop or compute bounds (e.g., those referenced around lines 1094, 1130,
1298) to take a stable snapshot Len := GetLength after calling SyncBufferData
(or ensure SyncBufferData has run), then use Len instead of FLength for bounds
checks, loop limits and any math that also reads FBufferData/FByteOffset; also
ensure any direct reads of FBufferData occur only after SyncBufferData so the
snapshot and buffer view are consistent (use BytesPerElement(FKind) and
HasValidBackingRange only via GetLength or with Len).

In `@source/units/Goccia.VM.CallFrame.pas`:
- Line 21: The initialization of TGocciaVMCallFrame using FillChar(AFrame,
SizeOf(AFrame), 0) is unsafe now that TGocciaVMCallFrame contains a managed
field (Arguments: TGocciaRegisterArray); replace the FillChar usage with a
structured initialization such as AFrame := Default(TGocciaVMCallFrame) or
explicitly set each field to its zero/default value so that dynamic-array
finalization/initialization is handled correctly and avoids memory corruption.

In `@source/units/Goccia.VM.pas`:
- Around line 9575-9582: The OP_DELETE_GLOBAL handler currently returns false
when the global binding is absent; change it so that if the binding is absent
but the engine is in sloppy (non-strict) mode it returns true (i.e., set
FRegisters[A] := RegisterBoolean(True)), while still returning false only for
existing non-deletable bindings; locate the strictness flag used elsewhere in
this VM (the same flag/field the runtime uses for strict mode checks) and
consult it in OP_DELETE_GLOBAL before deciding the result from
FGlobalScope.DeleteBinding(GlobalName).
- Around line 9553-9564: The OP_SET_GLOBAL_LOOSE path currently throws via
ThrowReferenceError when FGlobalScope.Contains(GlobalName) is false; change this
so that in sloppy (non-strict) mode you create/update the global property
instead of throwing: when FGlobalScope is assigned and Contains(GlobalName) is
false, call FGlobalScope.AssignBinding(GlobalName,
RegisterToValue(FRegisters[A]), 0, 0, True) (the same form used when the name
exists) rather than ThrowReferenceError; keep the ThrowReferenceError for strict
mode (guard with your VM's strict-mode flag used elsewhere) and continue to
obtain the name via
Template.GetConstantUnchecked(DecodeBx(Instruction)).StringValue and the value
via RegisterToValue(FRegisters[A]).

---

Outside diff comments:
In `@source/units/Goccia.Compiler.Expressions.pas`:
- Around line 1243-1308: The helper EmitBindingAssignmentFromRegister fails to
target "with" bindings for identifier patterns during assignments; before
resolving locals/upvalues, add a branch that when AAssignmentMode is true and
ShouldTryWithBinding(ACtx, AName) (or the existing predicate used elsewhere)
returns true, call EmitWithAssignmentOrFallback(ACtx, AName, AValueReg) (or the
existing helper signature) and Exit so the assignment writes the with-bound
property; make the same change at the other similar site referenced by
EmitDestructuring to ensure destructuring identifiers use with-bindings in
assignment mode.

In `@source/units/Goccia.Parser.pas`:
- Around line 3299-3328: The current branch that treats a non-strict `let`
followed by a later-line `{` as an identifier statement (the Check(gttLet) +
FNonStrictModeEnabled + FAutomaticSemicolonInsertion + (FTokens[FCurrent +
1].TokenType = gttLeftBrace) + (Peek.Line < FTokens[FCurrent + 1].Line) block)
is too broad and breaks valid sloppy `let` lexical declarations; restrict this
behavior so it runs only when resolving the specific `with`-body ambiguity.
Replace the broad condition with one that also checks a dedicated disambiguation
predicate (e.g. call or add IsWithBodyAmbiguity/ShouldTreatLetAsWithBody at the
same spot) and only create the
TGocciaExpressionStatement/TGocciaIdentifierExpression when that predicate is
true, leaving normal `let` declaration parsing untouched otherwise.

---

Nitpick comments:
In `@tests/language/non-strict-mode/assignment.js`:
- Around line 83-96: The test creates a non-writable global property
__gocciaNonStrictFixedAssignment on globalThis but leaves deletion to run only
on the success path; make cleanup exception-safe by wrapping the setup and
assertions in a try/finally block and perform delete
globalThis.__gocciaNonStrictFixedAssignment in the finally clause so the global
is removed regardless of test failures; locate the test function (the test(...)
block that defines __gocciaNonStrictFixedAssignment) and modify it to ensure the
delete happens in finally.
🪄 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: ea5f7a95-b293-43cc-8120-a6ad5fb84512

📥 Commits

Reviewing files that changed from the base of the PR and between fcb41a4 and b11f5dc.

📒 Files selected for processing (81)
  • docs/build-system.md
  • docs/bytecode-vm.md
  • docs/decision-log.md
  • docs/embedding.md
  • docs/errors.md
  • docs/goals.md
  • docs/interpreter.md
  • docs/language-tables.md
  • docs/language.md
  • docs/test262.md
  • docs/testing.md
  • docs/tutorial.md
  • docs/value-system.md
  • scripts/run_test262_suite.ts
  • scripts/test-cli-apps.ts
  • scripts/test-cli-config.ts
  • scripts/test-cli.ts
  • source/app/Goccia.CLI.Application.pas
  • source/app/GocciaBenchmarkRunner.dpr
  • source/app/GocciaBundler.dpr
  • source/app/GocciaREPL.dpr
  • source/app/GocciaScriptLoader.dpr
  • source/app/GocciaScriptLoaderBare.dpr
  • source/app/GocciaTestRunner.dpr
  • source/shared/CLI.Options.pas
  • source/units/Goccia.AST.BindingPatterns.pas
  • source/units/Goccia.AST.Expressions.pas
  • source/units/Goccia.AST.Statements.pas
  • source/units/Goccia.Bytecode.Binary.pas
  • source/units/Goccia.Bytecode.Chunk.pas
  • source/units/Goccia.Bytecode.OpCodeNames.pas
  • source/units/Goccia.Bytecode.pas
  • source/units/Goccia.Compiler.ConstantFolding.pas
  • source/units/Goccia.Compiler.Context.pas
  • source/units/Goccia.Compiler.Expressions.pas
  • source/units/Goccia.Compiler.Scope.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Compiler.pas
  • source/units/Goccia.Constants.PropertyNames.pas
  • source/units/Goccia.Constants.pas
  • source/units/Goccia.Engine.Backend.pas
  • source/units/Goccia.Engine.pas
  • source/units/Goccia.Evaluator.Assignment.pas
  • source/units/Goccia.Evaluator.Context.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.Interpreter.pas
  • source/units/Goccia.Modules.Loader.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.Scope.pas
  • source/units/Goccia.VM.CallFrame.pas
  • source/units/Goccia.VM.pas
  • source/units/Goccia.Values.ArgumentsObjectValue.pas
  • source/units/Goccia.Values.ArrayValue.pas
  • source/units/Goccia.Values.FunctionBase.pas
  • source/units/Goccia.Values.FunctionValue.pas
  • source/units/Goccia.Values.GeneratorValue.pas
  • source/units/Goccia.Values.ObjectValue.pas
  • source/units/Goccia.Values.ProxyValue.pas
  • source/units/Goccia.Values.TypedArrayValue.pas
  • tests/built-ins/Array/array-modification.js
  • tests/built-ins/Proxy/set.js
  • tests/built-ins/TypedArray/element-access.js
  • tests/built-ins/TypedArray/non-strict/element-access.js
  • tests/built-ins/TypedArray/non-strict/goccia.json
  • tests/language/arguments/arguments-object.js
  • tests/language/arguments/goccia.json
  • tests/language/arguments/strict-directive-arguments.js
  • tests/language/non-strict-mode-defaults/arguments-disabled.js
  • tests/language/non-strict-mode-defaults/assignment-disabled.js
  • tests/language/non-strict-mode-defaults/goccia.json
  • tests/language/non-strict-mode-modules/goccia.json
  • tests/language/non-strict-mode-modules/this-binding.js
  • tests/language/non-strict-mode/array-length.js
  • tests/language/non-strict-mode/assignment.js
  • tests/language/non-strict-mode/delete.js
  • tests/language/non-strict-mode/goccia.json
  • tests/language/non-strict-mode/this-binding.js
  • tests/language/non-strict-mode/use-strict-directive.js
  • tests/language/statements/unsupported-features.js
  • tests/language/with/goccia.json
  • tests/language/with/with-statement.js
💤 Files with no reviewable changes (1)
  • docs/errors.md

Comment thread docs/value-system.md Outdated
Comment thread scripts/test-cli.ts Outdated
Comment thread source/app/GocciaBundler.dpr
Comment thread source/units/Goccia.AST.BindingPatterns.pas
Comment thread source/units/Goccia.Compiler.Expressions.pas
Comment thread source/units/Goccia.Values.FunctionValue.pas
Comment thread source/units/Goccia.Values.TypedArrayValue.pas
Comment thread source/units/Goccia.VM.CallFrame.pas
Comment thread source/units/Goccia.VM.pas
Comment thread source/units/Goccia.VM.pas
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (2)
source/units/Goccia.Evaluator.pas (1)

5592-5617: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve this for private-member tag expressions.

Line 5609 mirrors the identifier fix, but tagged templates can also be driven by TGocciaPrivateMemberExpression. That path still falls through to EvaluateExpression(...) and forces ThisValue := undefined, so obj.#tag\x`won’t receiveobjas the receiver even thoughobj.#tag()` now does.

Suggested fix
 function EvaluateTaggedTemplate(const ATaggedTemplateExpression: TGocciaTaggedTemplateExpression; const AContext: TGocciaEvaluationContext): TGocciaValue;
 var
   Callee, ThisValue, ExprValue, TemplateObject: TGocciaValue;
   MemberExpr: TGocciaMemberExpression;
+  PrivateMemberExpr: TGocciaPrivateMemberExpression;
   CookedArray, RawArray: TGocciaArrayValue;
   Arguments: TGocciaArgumentsCollection;
   I: Integer;
   CalleeName: string;
 begin
@@
   if ATaggedTemplateExpression.Tag is TGocciaMemberExpression then
   begin
     MemberExpr := TGocciaMemberExpression(ATaggedTemplateExpression.Tag);
     Callee := EvaluateMember(MemberExpr, AContext, ThisValue);
   end
+  else if ATaggedTemplateExpression.Tag is TGocciaPrivateMemberExpression then
+  begin
+    PrivateMemberExpr := TGocciaPrivateMemberExpression(
+      ATaggedTemplateExpression.Tag);
+    Callee := EvaluatePrivateMember(PrivateMemberExpr, AContext, ThisValue);
+  end
   else if ATaggedTemplateExpression.Tag is TGocciaIdentifierExpression then
     AContext.Scope.ResolveIdentifierReference(
       TGocciaIdentifierExpression(ATaggedTemplateExpression.Tag).Name,
       Callee, ThisValue)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Evaluator.pas` around lines 5592 - 5617, The
tagged-template tag evaluation currently treats private-member tags like other
expressions and falls through to EvaluateExpression which sets ThisValue to
undefined; update the tag handling so that when ATaggedTemplateExpression.Tag is
a TGocciaPrivateMemberExpression you evaluate it similarly to
TGocciaMemberExpression: cast to TGocciaPrivateMemberExpression, call the
appropriate member evaluation (same codepath used by EvaluateMember) to obtain
Callee and preserve ThisValue (do not overwrite ThisValue with Undefined),
ensuring obj.#tag`...` receives obj as the receiver.
source/units/Goccia.Compiler.Expressions.pas (1)

2798-2846: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fallback path incorrectly uses method call semantics.

When the tag is an identifier that might be in a with-binding, AIsMethodCall is set to True unconditionally at line 2802. If no with-object actually contains the binding, the fallback path (line 2830) loads the identifier but AObjReg remains undefined. The caller then emits OP_CALL_METHOD with undefined as receiver, which is incorrect—the non-method fallback should use OP_CALL instead.

Compare with TryCompileWithIdentifierCall which correctly calls EmitBranchCall(True, ...) for with-binding hits and EmitBranchCall(False, ...) for the fallback.

Suggested approach

Restructure to emit the call within CompileTagCalleeRegisters (or a new helper), using OP_CALL_METHOD for with-binding paths and OP_CALL for the fallback, similar to TryCompileWithIdentifierCall. Alternatively, return a runtime flag indicating whether a with-binding was found and let the caller branch on it—but that would require additional bytecode.

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

In `@source/units/Goccia.Compiler.Expressions.pas` around lines 2798 - 2846, The
code sets AIsMethodCall := True for identifier tags before actually finding a
with-binding, so the fallback path leaves AObjReg undefined and causes callers
to emit OP_CALL_METHOD incorrectly; change the logic in the
TGoccia.Compiler.Expressions block that handles TGocciaIdentifierExpression so
that you only mark or emit a method call when a with-binding is actually found
(i.e., when EndCount > 0), and for the fallback path emit/indicate a normal call
(OP_CALL) instead of a method call (OP_CALL_METHOD). Concretely: either move
call emission into this block like TryCompileWithIdentifierCall does (use
EmitBranchCall/EmitCall variants to emit OP_CALL_METHOD for the with-binding
branches and OP_CALL for the fallback), or keep the current API but set
AIsMethodCall := (EndCount > 0) and ensure AObjReg is only allocated/used when
EndCount > 0 so the caller receives the correct call semantics; reference
IdentExpr, AObjReg, ABaseReg, EndCount, CompileIdentifierAccessNoWith and
TryCompileWithIdentifierCall to locate and adjust the behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Compiler.Expressions.pas`:
- Around line 1245-1255: DeclareArgumentsObjectLocal currently only checks the
global ACtx.CompatibilityNonStrictMode and therefore can incorrectly create an
implicit arguments binding for functions that are themselves strict; modify
DeclareArgumentsObjectLocal to accept an extra boolean parameter (e.g.,
AChildStrict) and only allow creating the implicit IDENTIFIER_ARGUMENTS local
when ACtx.CompatibilityNonStrictMode is true AND AChildStrict is false. Update
every call site to pass the child function's strictness (use
ChildTemplate.StrictCode at call sites where the child template is available)
and reference the existing symbols DeclareArgumentsObjectLocal,
ACtx.CompatibilityNonStrictMode, IDENTIFIER_ARGUMENTS and
ChildTemplate.StrictCode to locate places needing change.

In `@source/units/Goccia.Values.ArrayValue.pas`:
- Around line 2861-2865: The code currently frees ADescriptor when
DeleteSparseArrayIndexesAtOrAbove(Self, NewLen) returns false, which violates
DefineProperty's ownership contract and can cause a double-free; remove the
ADescriptor.Free call from that error path so the descriptor remains owned by
the caller, leaving the subsequent
ThrowTypeError(Format(SErrorCannotRedefineNonConfigurable, [AName]),
SSuggestCannotDeleteNonConfigurable) unchanged; ensure no other early-return
paths free ADescriptor in this block so ownership semantics for ADescriptor (and
callers that free it in their except handlers) remain consistent.

---

Outside diff comments:
In `@source/units/Goccia.Compiler.Expressions.pas`:
- Around line 2798-2846: The code sets AIsMethodCall := True for identifier tags
before actually finding a with-binding, so the fallback path leaves AObjReg
undefined and causes callers to emit OP_CALL_METHOD incorrectly; change the
logic in the TGoccia.Compiler.Expressions block that handles
TGocciaIdentifierExpression so that you only mark or emit a method call when a
with-binding is actually found (i.e., when EndCount > 0), and for the fallback
path emit/indicate a normal call (OP_CALL) instead of a method call
(OP_CALL_METHOD). Concretely: either move call emission into this block like
TryCompileWithIdentifierCall does (use EmitBranchCall/EmitCall variants to emit
OP_CALL_METHOD for the with-binding branches and OP_CALL for the fallback), or
keep the current API but set AIsMethodCall := (EndCount > 0) and ensure AObjReg
is only allocated/used when EndCount > 0 so the caller receives the correct call
semantics; reference IdentExpr, AObjReg, ABaseReg, EndCount,
CompileIdentifierAccessNoWith and TryCompileWithIdentifierCall to locate and
adjust the behavior.

In `@source/units/Goccia.Evaluator.pas`:
- Around line 5592-5617: The tagged-template tag evaluation currently treats
private-member tags like other expressions and falls through to
EvaluateExpression which sets ThisValue to undefined; update the tag handling so
that when ATaggedTemplateExpression.Tag is a TGocciaPrivateMemberExpression you
evaluate it similarly to TGocciaMemberExpression: cast to
TGocciaPrivateMemberExpression, call the appropriate member evaluation (same
codepath used by EvaluateMember) to obtain Callee and preserve ThisValue (do not
overwrite ThisValue with Undefined), ensuring obj.#tag`...` receives obj as the
receiver.
🪄 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: 2e58c2c6-8b6b-4045-b392-8dcb12092217

📥 Commits

Reviewing files that changed from the base of the PR and between b11f5dc and 30b53c8.

📒 Files selected for processing (12)
  • docs/value-system.md
  • scripts/test-cli.ts
  • source/app/Goccia.CLI.Application.pas
  • source/app/GocciaBundler.dpr
  • source/units/Goccia.AST.BindingPatterns.pas
  • source/units/Goccia.Compiler.Expressions.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Compiler.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.Scope.pas
  • source/units/Goccia.VM.pas
  • source/units/Goccia.Values.ArrayValue.pas
✅ Files skipped from review due to trivial changes (1)
  • docs/value-system.md
🚧 Files skipped from review as they are similar to previous changes (7)
  • source/units/Goccia.Compiler.pas
  • source/units/Goccia.AST.BindingPatterns.pas
  • source/app/Goccia.CLI.Application.pas
  • source/app/GocciaBundler.dpr
  • source/units/Goccia.Scope.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.VM.pas

Comment thread source/units/Goccia.Compiler.Expressions.pas
Comment thread source/units/Goccia.Values.ArrayValue.pas Outdated
Comment thread source/units/Goccia.Values.ArrayValue.pas Outdated
@frostney frostney merged commit d7b9547 into main May 17, 2026
60 of 61 checks passed
@frostney frostney deleted the t3code/a72fd50b branch May 17, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compat flag for non-strict mode semantics (--compat-non-strict-mode)

1 participant