Skip to content

Fix computed class field yield evaluation#672

Open
frostney wants to merge 10 commits into
mainfrom
issue-652-computed-field-yield
Open

Fix computed class field yield evaluation#672
frostney wants to merge 10 commits into
mainfrom
issue-652-computed-field-yield

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented May 19, 2026

Summary

  • Resolve computed public class field names during class definition so yield resumes correctly for instance and static fields.
  • Preserve source-order computed key evaluation in bytecode and interpreter generator resumes.
  • Define computed public instance fields with class-field own-property semantics in bytecode, rather than assignment semantics.
  • Preserve source-order evaluation for duplicate static object-literal properties while keeping the final property value and insertion order semantics.
  • Keep named async function expression source text anchored at the async keyword.
  • Closes Evaluate yield in computed public class field names #652.

Testing

  • Verified no regressions and confirmed the new feature or bugfix in end-to-end JavaScript/TypeScript tests
    • ./build/GocciaTestRunner tests/language/classes/class-computed-field-yield-order.js tests/language/function-keyword/class-computed-field-yield.js tests/language/objects/basic-object-creation.js tests/language/function-keyword/async-function.js --asi (19/19)
    • ./build/GocciaTestRunner tests/language/classes/class-computed-field-yield-order.js tests/language/function-keyword/class-computed-field-yield.js tests/language/objects/basic-object-creation.js tests/language/function-keyword/async-function.js --asi --mode=bytecode (19/19)
    • ./build/GocciaTestRunner tests --asi --unsafe-ffi (9743/9743)
    • ./build/GocciaTestRunner tests --asi --unsafe-ffi --mode=bytecode (9743/9743)
    • bun scripts/run_test262_suite.ts --categories language --filter 'language/**/class/cpn-class-*-fields*computed-property-name-from-yield-expression.js' --mode interpreted --jobs 1 --verbose --timeout-ms 10000 (4/4)
    • bun scripts/run_test262_suite.ts --categories language --filter 'language/**/class/cpn-class-*-fields*computed-property-name-from-yield-expression.js' --mode bytecode --jobs 1 --verbose --timeout-ms 10000 (4/4)
  • Updated documentation (not needed; these are spec-compliance engine fixes covered by tests)
  • Optional: Verified no regressions and confirmed the new feature or bugfix in native Pascal tests (if AST, scope, evaluator, or value types changed)
    • ./build.pas tests loaderbare bundler
  • Optional: Verified no benchmark regressions or confirmed benchmark coverage for the change
  • Additional checks:
    • ./format.pas --check
    • git diff --cached --check
    • ./build.pas clean testrunner
    • ./build/GocciaBundler tests/language/objects/basic-object-creation.js --output=/tmp/goccia-object-basic.gbc

@vercel
Copy link
Copy Markdown

vercel Bot commented May 19, 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 20, 2026 11:12pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 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: d2be3f9e-ed44-4df4-a045-821db114aaef

📥 Commits

Reviewing files that changed from the base of the PR and between 4e7c94f and 6db50cb.

📒 Files selected for processing (4)
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.VM.pas
  • tests/language/decorators/auto-accessor-decorator.js
  • tests/language/decorators/basic-class-decorator.js

📝 Walkthrough

Walkthrough

Adds parser/AST metadata, compiler captured-key locals and opcodes, VM/runtime decorator and dynamic-define handling, evaluator replay/initialization for computed class field keys, GC/continuation helpers, and tests for computed public class-field semantics.

Changes

Computed Public Class Fields

Layer / File(s) Summary
AST and class value records
source/units/Goccia.AST.Statements.pas, source/units/Goccia.Values.ClassValue.pas
Add computed-field metadata (IsComputed, ElementIndex, ComputedKeyExpression/ComputedKey, FieldInitializer/Initializer); add AddFieldInitializerWithKey/AddAutoAccessorWithKey and GC marking updates.
Parser: computed member capture
source/units/Goccia.Parser.pas
Record computed element state, computed-key expression, element index, and field initializer into cek elements and FieldOrder entries; fix async-function same-line position handling.
Compiler: computed-key locals & emission
source/units/Goccia.Compiler.Statements.pas
Introduce computed-key local types and deterministic naming, capture computed-key expressions in CompileComputedElements, thread locals through compilation, resolve keys for static/instance emission, and emit OP_TO_PROPERTY_KEY / OP_DEFINE_PROP_DYNAMIC where needed.
Compiler: decorator/accessor wiring
source/units/Goccia.Compiler.Statements.pas
Pass computed-key locals into decorator and accessor passes so decorator extra operands and auto-accessor setup carry captured computed-key slots for computed elements.
Bytecode and opcode names
source/units/Goccia.Bytecode.pas, source/units/Goccia.Bytecode.OpCodeNames.pas
Bump GOCCIA_FORMAT_VERSION to 37 and add OP_DEFINE_PROP_DYNAMIC, OP_SETUP_AUTO_ACCESSOR_DYNAMIC, and OP_TO_PROPERTY_KEY; add corresponding OpCodeName mappings.
VM: opcodes & decorator runtime
source/units/Goccia.VM.pas
Handle OP_TO_PROPERTY_KEY and OP_DEFINE_PROP_DYNAMIC, add SetupAutoAccessorValueByKey, make ApplyElementDecorator accept computed-key and perform symbol/name-aware lookups/defines, and forward computed-key operands in dispatch.
Evaluator & decorators: replay and initialization
source/units/Goccia.Evaluator.pas, source/units/Goccia.Evaluator.Decorators.pas
Evaluate and replay computed-key expressions across generator suspension (Continuation.Save/Take), map resolved keys into FieldOrder by element index, initialize instance/static/object computed fields using resolved symbol/string keys, construct decorator contexts/access helpers aware of computed keys, attach initializers with computed-key identity, and clear saved computed-key expression values after construction.
Generator continuation helper
source/units/Goccia.Generator.Continuation.pas
Add ClearCompletedExpressionValue to allow clearing saved completed expression values for computed-key replay bookkeeping.
Decorator accessor helpers
source/units/Goccia.Evaluator.Decorators.pas
Add AccessGetter/AccessSetter constructors that accept symbol-or-string keys and perform symbol-aware get/set behavior.
Tests: computed-field behavior and async
tests/language/classes/*, tests/language/decorators/*, tests/language/function-keyword/*
Add and extend tests for computed public field yield/order/coercion, decorator computed-key semantics (including symbol keys and auto-accessors), and async named function toString.

Sequence Diagram

sequenceDiagram
  participant Parser
  participant Compiler
  participant VM
  participant Evaluator
  Parser->>Compiler: emit element metadata (IsComputed, ComputedKeyExpression, ElementIndex)
  Compiler->>Compiler: allocate deterministic computed-key locals and capture key expressions
  Compiler->>VM: emit OP_TO_PROPERTY_KEY / OP_DEFINE_PROP_DYNAMIC and computed-key operand slots
  Evaluator->>Evaluator: evaluate computed key (may SaveCompletedExpressionValue and suspend on yield)
  Evaluator->>Evaluator: on resume TakeCompletedExpressionValue (reuse saved key)
  VM->>VM: OP_DEFINE_PROP_DYNAMIC defines property using computed key (symbol/string)
  Evaluator->>Evaluator: InitializeInstanceProperties uses resolved keys and runs decorator initializers
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix computed class field yield evaluation' accurately and concisely describes the main change: resolving computed public class field names during class definition to enable correct yield evaluation.
Description check ✅ Passed The PR description provides a clear summary addressing all key changes, includes explicit testing verification with multiple test suite runs, and references the closed issue #652. All required sections are present and adequately filled.
Linked Issues check ✅ Passed All code changes comprehensively address issue #652 requirements: yield expressions in computed class field names now suspend/resume correctly, source-order evaluation is preserved across interpreter/bytecode modes, and instance fields use proper semantics.
Out of Scope Changes check ✅ Passed All changes are scoped to computed public class field support: parser enhancements, evaluator computed-field handling, bytecode opcodes/format version updates, VM implementation, and comprehensive test coverage. The async function source-text anchoring fix is directly related to class field evaluation testing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

Suite Timing

Test Runner (interpreted: 9,766 passed; bytecode: 9,766 passed)
Metric Interpreted Bytecode
Total 9766 9766
Passed 9766 ✅ 9766 ✅
Workers 4 4
Test Duration 2.80s 3.11s
Lex (cumulative) 397.0ms 345.7ms
Parse (cumulative) 272.4ms 293.4ms
Compile (cumulative) 634.7ms
Execute (cumulative) 2.67s 2.67s
Engine Total (cumulative) 3.34s 3.95s
Lex (avg/worker) 99.2ms 86.4ms
Parse (avg/worker) 68.1ms 73.4ms
Compile (avg/worker) 158.7ms
Execute (avg/worker) 668.4ms 668.6ms
Engine Total (avg/worker) 835.7ms 987.1ms

Memory

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

Metric Interpreted Bytecode
GC Live 280.06 MiB 274.35 MiB
GC Peak Live 280.06 MiB 274.36 MiB
GC Allocated During Run 284.52 MiB 278.81 MiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 1 1
GC Collected Objects 88 88
Heap Start Allocated 158.3 KiB 158.3 KiB
Heap End Allocated 1.53 MiB 1.53 MiB
Heap Delta Allocated 1.38 MiB 1.38 MiB
Heap Delta Free 703.7 KiB 703.7 KiB
Benchmarks (interpreted: 407; bytecode: 407)
Metric Interpreted Bytecode
Total 407 407
Workers 4 4
Duration 2.46min 2.37min

Memory

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

Metric Interpreted Bytecode
GC Live 3.93 MiB 3.93 MiB
GC Peak Live 109.44 MiB 83.45 MiB
GC Allocated During Run 14.84 GiB 10.55 GiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 2,815 2,666
GC Collected Objects 268,292,691 246,133,217
Heap Start Allocated 1.27 MiB 1.27 MiB
Heap End Allocated 1.27 MiB 1.27 MiB
Heap Delta Allocated 128 B 128 B

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

Benchmark Results

407 benchmarks

Interpreted: 🟢 54 improved · 🔴 201 regressed · 152 unchanged · avg -2.8%
Bytecode: 🟢 48 improved · 🔴 117 regressed · 242 unchanged · avg -1.1%

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

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

test262 Conformance

Category Run Passed Δ Pass Failed Pass-rate Δ Rate
built-ins 23,449 16,067 +1 7,377 68.5% ±0pp
harness 116 72 ±0 44 62.1% ±0pp
intl402 3,324 892 ±0 2,432 26.8% ±0pp
language 23,635 14,429 +226 9,206 61.0% +1.0pp
staging 1,484 571 ±0 910 38.5% ±0pp
total 52,008 32,031 +227 19,969 61.6% +0.4pp

Areas closest to 100%

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

Newly passing (227):

  • built-ins/Number/prototype/toExponential/undefined-fractiondigits.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-additive-expression-add.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-additive-expression-subtract.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-arrow-function-expression.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-assignment-expression-assignment.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-assignment-expression-bitwise-or.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-assignment-expression-coalesce.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-assignment-expression-logical-and.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-assignment-expression-logical-or.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-async-arrow-function-expression.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-await-expression.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-condition-expression-false.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-condition-expression-true.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-decimal-e-notational-literal.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-decimal-literal.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-exponetiation-expression.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-expression-coalesce.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-expression-logical-and.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-expression-logical-or.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-function-declaration.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-function-expression.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-identifier.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-integer-e-notational-literal.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-integer-separators.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-math.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-multiplicative-expression-div.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-multiplicative-expression-mult.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-null.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-numeric-literal.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-string-literal.js
  • language/expressions/class/cpn-class-expr-fields-computed-property-name-from-yield-expression.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-additive-expression-add.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-additive-expression-subtract.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-arrow-function-expression.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-assignment-expression-assignment.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-assignment-expression-bitwise-or.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-assignment-expression-coalesce.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-assignment-expression-logical-and.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-assignment-expression-logical-or.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-async-arrow-function-expression.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-await-expression.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-condition-expression-false.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-condition-expression-true.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-decimal-e-notational-literal.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-decimal-literal.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-exponetiation-expression.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-expression-coalesce.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-expression-logical-and.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-expression-logical-or.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-function-declaration.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-function-expression.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-identifier.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-integer-e-notational-literal.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-integer-separators.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-math.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-multiplicative-expression-div.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-multiplicative-expression-mult.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-null.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-numeric-literal.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-string-literal.js
  • language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-yield-expression.js
  • language/expressions/class/elements/after-same-line-gen-computed-names.js
  • language/expressions/class/elements/after-same-line-gen-computed-symbol-names.js
  • language/expressions/class/elements/after-same-line-method-computed-names.js
  • language/expressions/class/elements/after-same-line-method-computed-symbol-names.js
  • language/expressions/class/elements/after-same-line-static-async-gen-computed-names.js
  • language/expressions/class/elements/after-same-line-static-async-gen-computed-symbol-names.js
  • language/expressions/class/elements/after-same-line-static-async-method-computed-names.js
  • language/expressions/class/elements/after-same-line-static-async-method-computed-symbol-names.js
  • language/expressions/class/elements/after-same-line-static-gen-computed-names.js
  • language/expressions/class/elements/after-same-line-static-gen-computed-symbol-names.js
  • language/expressions/class/elements/after-same-line-static-method-computed-names.js
  • language/expressions/class/elements/after-same-line-static-method-computed-symbol-names.js
  • language/expressions/class/elements/computed-name-toprimitive-symbol.js
  • language/expressions/class/elements/computed-name-toprimitive.js
  • language/expressions/class/elements/evaluation-error/computed-name-referenceerror.js
  • language/expressions/class/elements/evaluation-error/computed-name-toprimitive-err.js
  • language/expressions/class/elements/evaluation-error/computed-name-toprimitive-returns-noncallable.js
  • language/expressions/class/elements/evaluation-error/computed-name-toprimitive-returns-nonobject.js
  • language/expressions/class/elements/evaluation-error/computed-name-tostring-err.js
  • language/expressions/class/elements/evaluation-error/computed-name-valueof-err.js
  • language/expressions/class/elements/field-declaration.js
  • language/expressions/class/elements/init-value-defined-after-class.js
  • language/expressions/class/elements/init-value-incremental.js
  • language/expressions/class/elements/intercalated-static-non-static-computed-fields.js
  • language/expressions/class/elements/multiple-definitions-computed-names.js
  • language/expressions/class/elements/multiple-definitions-computed-symbol-names.js
  • language/expressions/class/elements/multiple-stacked-definitions-computed-names.js
  • language/expressions/class/elements/multiple-stacked-definitions-computed-symbol-names.js
  • language/expressions/class/elements/new-no-sc-line-method-computed-names.js
  • language/expressions/class/elements/new-no-sc-line-method-computed-symbol-names.js
  • language/expressions/class/elements/new-sc-line-gen-computed-names.js
  • language/expressions/class/elements/new-sc-line-gen-computed-symbol-names.js
  • language/expressions/class/elements/new-sc-line-method-computed-names.js
  • language/expressions/class/elements/new-sc-line-method-computed-symbol-names.js
  • language/expressions/class/elements/redeclaration-symbol.js
  • language/expressions/class/elements/regular-definitions-computed-names.js
  • language/expressions/class/elements/regular-definitions-computed-symbol-names.js
  • language/expressions/class/elements/same-line-async-gen-computed-names.js
  • language/expressions/class/elements/same-line-async-gen-computed-symbol-names.js
  • … 127 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 frostney force-pushed the issue-652-computed-field-yield branch 3 times, most recently from 6535227 to 5047e18 Compare May 20, 2026 07:47
Resolve computed public class field names during class definition so yield resumes correctly for instance and static fields in both interpreted and bytecode execution.
@frostney frostney force-pushed the issue-652-computed-field-yield branch from 5047e18 to 6d5fe06 Compare May 20, 2026 07:49
@frostney frostney marked this pull request as ready for review May 20, 2026 11:51
@coderabbitai coderabbitai Bot added bug Something isn't working spec compliance Mismatch against official JavaScript/TypeScript specification internal Refactoring, CI, tooling, cleanup labels May 20, 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: 1

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 (2)

4413-4424: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Merge an existing symbol setter when emitting a computed getter.

The symbol-keyed getter path always overwrites the descriptor with a getter-only accessor. That means set [s](...) {} followed by get [s]() {} loses the setter, even though the setter path below already does the symmetric merge in the opposite order.

Suggested fix
       cekGetter:
       begin
         GetterFunction := BuildClassGetter(Elem.GetterNode);
         if ComputedKey is TGocciaSymbolValue then
         begin
-          if Elem.IsStatic then
-            ClassValue.DefineSymbolProperty(
-              TGocciaSymbolValue(ComputedKey),
-              TGocciaPropertyDescriptorAccessor.Create(GetterFunction, nil, [pfConfigurable]))
-          else
-            ClassValue.Prototype.DefineSymbolProperty(
-              TGocciaSymbolValue(ComputedKey),
-              TGocciaPropertyDescriptorAccessor.Create(GetterFunction, nil, [pfConfigurable]));
+          if Elem.IsStatic then
+            ExistingDescriptor := ClassValue.GetOwnStaticSymbolDescriptor(
+              TGocciaSymbolValue(ComputedKey))
+          else
+            ExistingDescriptor := ClassValue.Prototype.GetOwnSymbolPropertyDescriptor(
+              TGocciaSymbolValue(ComputedKey));
+
+          if (ExistingDescriptor is TGocciaPropertyDescriptorAccessor) and
+             Assigned(TGocciaPropertyDescriptorAccessor(ExistingDescriptor).Setter) then
+          begin
+            if Elem.IsStatic then
+              ClassValue.DefineSymbolProperty(
+                TGocciaSymbolValue(ComputedKey),
+                TGocciaPropertyDescriptorAccessor.Create(
+                  GetterFunction,
+                  TGocciaPropertyDescriptorAccessor(ExistingDescriptor).Setter,
+                  [pfConfigurable]))
+            else
+              ClassValue.Prototype.DefineSymbolProperty(
+                TGocciaSymbolValue(ComputedKey),
+                TGocciaPropertyDescriptorAccessor.Create(
+                  GetterFunction,
+                  TGocciaPropertyDescriptorAccessor(ExistingDescriptor).Setter,
+                  [pfConfigurable]));
+          end
+          else if Elem.IsStatic then
+            ClassValue.DefineSymbolProperty(
+              TGocciaSymbolValue(ComputedKey),
+              TGocciaPropertyDescriptorAccessor.Create(
+                GetterFunction, nil, [pfConfigurable]))
+          else
+            ClassValue.Prototype.DefineSymbolProperty(
+              TGocciaSymbolValue(ComputedKey),
+              TGocciaPropertyDescriptorAccessor.Create(
+                GetterFunction, nil, [pfConfigurable]));
         end

Also applies to: 4444-4469

🤖 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 4413 - 4424, When emitting a
computed symbol getter in the GetterFunction path (built by BuildClassGetter) do
not unconditionally overwrite existing symbol accessor descriptors; instead
fetch the current descriptor from ClassValue (or ClassValue.Prototype when
Elem.IsStatic is false) for the TGocciaSymbolValue(ComputedKey), detect if it's
an existing TGocciaPropertyDescriptorAccessor with a setter, and construct the
new TGocciaPropertyDescriptorAccessor reusing the existing setter and preserving
flags (e.g., pfConfigurable) so the setter is not lost; replace the direct
DefineSymbolProperty call in the GetterFunction branch with logic that merges
the existing setter into the new getter accessor (mirroring the merge already
done in the setter path).

3529-3559: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unify the field-order path for all public instance fields.

Only the new computed branch materializes declaration-only fields as undefined and uses own-property definition. The fallback public branch still skips x; when there is no initializer and still uses AssignProperty, so mixed classes like class C { [k]; x; } will drop x, and x = 1 still behaves differently from the computed branch by going through assignment semantics.

Suggested direction
-      else
-      begin
-        if AClassValue.InstancePropertyDefs.TryGetValue(FOEntry.Name, Expr) and Assigned(Expr) then
-        begin
-          PropertyValue := EvaluateExpression(Expr, LocalContext);
-          AInstance.AssignProperty(FOEntry.Name, PropertyValue);
-        end;
-      end;
+      else
+      begin
+        if Assigned(FOEntry.Initializer) then
+          PropertyValue := EvaluateExpression(FOEntry.Initializer, LocalContext)
+        else
+          PropertyValue := TGocciaUndefinedLiteralValue.UndefinedValue;
+        AInstance.DefineProperty(
+          FOEntry.Name,
+          TGocciaPropertyDescriptorData.Create(
+            PropertyValue, [pfEnumerable, pfConfigurable, pfWritable]));
+      end;

Apply the same shape in InitializeObjectInstanceProperties, using AContext there.

Also applies to: 3609-3640

🤖 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 3529 - 3559, The
public-instance-field fallback path should mirror the computed-field branch:
when handling FOEntry that is not computed and not private, materialize
declaration-only fields as TGocciaUndefinedLiteralValue.UndefinedValue (instead
of skipping them) and define them as own-properties with
AInstance.DefineSymbolProperty or AInstance.DefineProperty (matching the
computed-key handling) rather than calling AInstance.AssignProperty; update the
logic in InitializeObjectInstanceProperties to use AContext and the same shape
as the computed branch so that fields like `x;` are created as undefined and
initializer-bearing fields still evaluate via
EvaluateExpression(LocalContext/AContext) before defining the property. Ensure
you reference FOEntry.ComputedKey / TGocciaSymbolValue, FOEntry.Name,
AClassValue.InstancePropertyDefs, EvaluateExpression, DefineSymbolProperty,
DefineProperty, AssignProperty (remove/replace), and SetPrivateProperty
accordingly.
🧹 Nitpick comments (1)
source/units/Goccia.Compiler.Statements.pas (1)

3825-3844: 💤 Low value

Consider using EncodeABC for consistency with existing OP_LOAD_UNDEFINED usage.

Line 3830 uses EncodeABx(OP_LOAD_UNDEFINED, ValReg, 0), but the existing codebase uses EncodeABC(OP_LOAD_UNDEFINED, ..., 0, 0) (e.g., lines 702, 1714). Both work functionally since the extra fields are zero, but EncodeABC would be more consistent.

Suggested fix for consistency
-          EmitInstruction(ChildCtx, EncodeABx(OP_LOAD_UNDEFINED, ValReg, 0));
+          EmitInstruction(ChildCtx, EncodeABC(OP_LOAD_UNDEFINED, ValReg, 0, 0));
🤖 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.Statements.pas` around lines 3825 - 3844,
Replace the inconsistent EncodeABx usage with EncodeABC for OP_LOAD_UNDEFINED:
in the block inside the loop where OP_LOAD_UNDEFINED is emitted (use of
EncodeABx(OP_LOAD_UNDEFINED, ValReg, 0)), change it to call
EncodeABC(OP_LOAD_UNDEFINED, ValReg, 0, 0) so it matches other sites (e.g.,
lines that use EncodeABC(OP_LOAD_UNDEFINED,...)), keeping the same ValReg
operand and zero immediates; this maintains consistency with existing EncodeABC
patterns while preserving 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.Evaluator.pas`:
- Around line 4483-4500: The decorator/initializer paths must use the resolved
computed keys the same way the field-order code does: when plumbing metadata or
attaching initializers for AClassDef.FFieldOrder entries, check
FieldOrderEntries[I].IsComputed and AClassDef.FFieldOrder[I].ElementIndex and,
if in range, use
ResolvedComputedFieldKeys[AClassDef.FFieldOrder[I].ElementIndex] instead of
falling back to Elem.Name; propagate that resolved key into whatever code
records decorator metadata/initializer associations (the same places you call
ClassValue.SetFieldOrder and the decorator pass logic referenced at the other
spots), so decorated computed fields receive the actual key (including symbol
keys) when creating metadata or binding initializers.

---

Outside diff comments:
In `@source/units/Goccia.Evaluator.pas`:
- Around line 4413-4424: When emitting a computed symbol getter in the
GetterFunction path (built by BuildClassGetter) do not unconditionally overwrite
existing symbol accessor descriptors; instead fetch the current descriptor from
ClassValue (or ClassValue.Prototype when Elem.IsStatic is false) for the
TGocciaSymbolValue(ComputedKey), detect if it's an existing
TGocciaPropertyDescriptorAccessor with a setter, and construct the new
TGocciaPropertyDescriptorAccessor reusing the existing setter and preserving
flags (e.g., pfConfigurable) so the setter is not lost; replace the direct
DefineSymbolProperty call in the GetterFunction branch with logic that merges
the existing setter into the new getter accessor (mirroring the merge already
done in the setter path).
- Around line 3529-3559: The public-instance-field fallback path should mirror
the computed-field branch: when handling FOEntry that is not computed and not
private, materialize declaration-only fields as
TGocciaUndefinedLiteralValue.UndefinedValue (instead of skipping them) and
define them as own-properties with AInstance.DefineSymbolProperty or
AInstance.DefineProperty (matching the computed-key handling) rather than
calling AInstance.AssignProperty; update the logic in
InitializeObjectInstanceProperties to use AContext and the same shape as the
computed branch so that fields like `x;` are created as undefined and
initializer-bearing fields still evaluate via
EvaluateExpression(LocalContext/AContext) before defining the property. Ensure
you reference FOEntry.ComputedKey / TGocciaSymbolValue, FOEntry.Name,
AClassValue.InstancePropertyDefs, EvaluateExpression, DefineSymbolProperty,
DefineProperty, AssignProperty (remove/replace), and SetPrivateProperty
accordingly.

---

Nitpick comments:
In `@source/units/Goccia.Compiler.Statements.pas`:
- Around line 3825-3844: Replace the inconsistent EncodeABx usage with EncodeABC
for OP_LOAD_UNDEFINED: in the block inside the loop where OP_LOAD_UNDEFINED is
emitted (use of EncodeABx(OP_LOAD_UNDEFINED, ValReg, 0)), change it to call
EncodeABC(OP_LOAD_UNDEFINED, ValReg, 0, 0) so it matches other sites (e.g.,
lines that use EncodeABC(OP_LOAD_UNDEFINED,...)), keeping the same ValReg
operand and zero immediates; this maintains consistency with existing EncodeABC
patterns while preserving behavior.
🪄 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: 2f9e68d3-07cc-46f8-abad-a2722b8061dd

📥 Commits

Reviewing files that changed from the base of the PR and between a1a0ad5 and 28a872d.

📒 Files selected for processing (11)
  • source/units/Goccia.AST.Statements.pas
  • source/units/Goccia.Bytecode.OpCodeNames.pas
  • source/units/Goccia.Bytecode.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.VM.pas
  • source/units/Goccia.Values.ClassValue.pas
  • tests/language/classes/class-computed-field-yield-order.js
  • tests/language/function-keyword/async-function.js
  • tests/language/function-keyword/class-computed-field-yield.js

Comment thread source/units/Goccia.Evaluator.pas
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

5981-6220: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use symbol-aware property access for decorated computed keys.

When AComputedKey is a symbol, this method records it in ElementKey but still reads and rewrites the element through ElementName string APIs. That makes decorated [@@sym] methods/getters/setters/accessors target a string-named property instead of the symbol slot, and context.access points at the wrong member. Route those branches through the symbol-specific getters/setters/define paths whenever ElementKey is TGocciaSymbolValue.

🤖 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.VM.pas` around lines 5981 - 6220, The code records symbol
computed keys into ElementKey but continues to use ElementName/string-based APIs
(e.g., calls to TGocciaClassValue.GetProperty,
Prototype.GetOwnPropertyDescriptor, SetProperty, DefineProperty,
AddStaticGetter/AddStaticSetter, AddGetter/AddSetter,
AddFieldInitializerWithKey) which causes decorated symbol-keyed members to be
read/written by name; update all branches that access or modify the element (the
places that set ElementValue, call
GetProperty/GetOwnPropertyDescriptor/DefineProperty, SetProperty, and the Add...
methods when applying the decorator result) to check if ElementKey is a
TGocciaSymbolValue and, when so, use the symbol-aware APIs (symbol overloads or
the symbol-specific getters/setters/define methods on
TGocciaClassValue/Prototype and AddFieldInitializerWithKey with ElementKey)
instead of the string-based ElementName paths so the decorator targets the
symbol slot and context.access points to the correct member.
source/units/Goccia.Compiler.Statements.pas (1)

3720-3728: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the computed-field key local unique per class.

Line 3723 derives the hidden local name from only I. A second computed-field class in the same compiler scope will reuse names like #computed-field-key:0, and the later ResolveLocal/ResolveUpvalue calls in CompileFieldInitializer, static field emission, and decorator orchestration can bind the wrong captured key.

Illustrative fix
-      ComputedKeyName := Format('`#computed-field-key`:%d', [I]);
+      ComputedKeyName := Format('`#computed-field-key`:%d:%d',
+        [CurrentCodePosition(ACtx), I]);
🤖 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.Statements.pas` around lines 3720 - 3728, The
computed-field hidden local name is only based on I (ComputedKeyName :=
Format('`#computed-field-key`:%d',[I])) so different classes in the same compiler
scope can collide; change the name generation in the cekField branch to include
a class-unique identifier (for example the current class symbol/name or
scope/class id accessible from ACtx or the element owner) so ComputedKeyName
becomes unique per class before calling ACtx.Scope.DeclareLocal; update any
related logic that expects that name (CompileFieldInitializer,
ResolveLocal/ResolveUpvalue, static field emission and decorator orchestration)
to rely on the new class-scoped ComputedKeyName.
♻️ Duplicate comments (1)
source/units/Goccia.Evaluator.pas (1)

4625-4643: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

A related computed-key decorator gap is still open.

ResolvedComputedFieldKeys is field-only, but this path now consults it for every computed decorated element. That leaves ElementKey = nil for computed methods/getters/setters/accessors, so context.name and initializer bookkeeping still fall back to Elem.Name. For symbol-keyed computed fields/accessors, context.access also stays string-based because ElementName is never updated from the resolved key. Please carry a per-element resolved key through this pass and use it consistently for context.name, context.access, and AddFieldInitializerWithKey(...).

Also applies to: 4801-4801, 4830-4830

🤖 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 4625 - 4643, The code only
reads ResolvedComputedFieldKeys (a field-only array) and sets
ElementKey/ElementName for computed fields, leaving computed
methods/getters/setters/accessors with nil ElementKey and string ElementName;
fix by carrying a per-element resolved key (e.g., local PerElementResolvedKey)
for every Elem when you detect Elem.IsComputed (not just field elements),
populate it from ResolvedComputedFieldKeys when available or from the element's
resolved symbol value when the element is a symbol-keyed computed
accessor/method, and then use that PerElementResolvedKey everywhere you
currently use ElementKey/ElementName — update
ContextObject.AssignProperty(PROP_NAME,...), the context.access assignment, and
the call to AddFieldInitializerWithKey(...) to consume the per-element resolved
key instead of falling back to Elem.Name so symbol-keyed computed
accessors/methods correctly carry symbol keys through.
🤖 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.

Outside diff comments:
In `@source/units/Goccia.Compiler.Statements.pas`:
- Around line 3720-3728: The computed-field hidden local name is only based on I
(ComputedKeyName := Format('`#computed-field-key`:%d',[I])) so different classes
in the same compiler scope can collide; change the name generation in the
cekField branch to include a class-unique identifier (for example the current
class symbol/name or scope/class id accessible from ACtx or the element owner)
so ComputedKeyName becomes unique per class before calling
ACtx.Scope.DeclareLocal; update any related logic that expects that name
(CompileFieldInitializer, ResolveLocal/ResolveUpvalue, static field emission and
decorator orchestration) to rely on the new class-scoped ComputedKeyName.

In `@source/units/Goccia.VM.pas`:
- Around line 5981-6220: The code records symbol computed keys into ElementKey
but continues to use ElementName/string-based APIs (e.g., calls to
TGocciaClassValue.GetProperty, Prototype.GetOwnPropertyDescriptor, SetProperty,
DefineProperty, AddStaticGetter/AddStaticSetter, AddGetter/AddSetter,
AddFieldInitializerWithKey) which causes decorated symbol-keyed members to be
read/written by name; update all branches that access or modify the element (the
places that set ElementValue, call
GetProperty/GetOwnPropertyDescriptor/DefineProperty, SetProperty, and the Add...
methods when applying the decorator result) to check if ElementKey is a
TGocciaSymbolValue and, when so, use the symbol-aware APIs (symbol overloads or
the symbol-specific getters/setters/define methods on
TGocciaClassValue/Prototype and AddFieldInitializerWithKey with ElementKey)
instead of the string-based ElementName paths so the decorator targets the
symbol slot and context.access points to the correct member.

---

Duplicate comments:
In `@source/units/Goccia.Evaluator.pas`:
- Around line 4625-4643: The code only reads ResolvedComputedFieldKeys (a
field-only array) and sets ElementKey/ElementName for computed fields, leaving
computed methods/getters/setters/accessors with nil ElementKey and string
ElementName; fix by carrying a per-element resolved key (e.g., local
PerElementResolvedKey) for every Elem when you detect Elem.IsComputed (not just
field elements), populate it from ResolvedComputedFieldKeys when available or
from the element's resolved symbol value when the element is a symbol-keyed
computed accessor/method, and then use that PerElementResolvedKey everywhere you
currently use ElementKey/ElementName — update
ContextObject.AssignProperty(PROP_NAME,...), the context.access assignment, and
the call to AddFieldInitializerWithKey(...) to consume the per-element resolved
key instead of falling back to Elem.Name so symbol-keyed computed
accessors/methods correctly carry symbol keys through.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7e51e346-6040-4025-9fc0-b750fcd224e9

📥 Commits

Reviewing files that changed from the base of the PR and between 021776f and 92b4b9e.

📒 Files selected for processing (5)
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.VM.pas
  • source/units/Goccia.Values.ClassValue.pas
  • tests/language/decorators/computed-field-decorator.js
✅ Files skipped from review due to trivial changes (1)
  • tests/language/decorators/computed-field-decorator.js

- Preserve computed property keys across fields, methods, getters, and setters
- Support symbol keys in decorator access helpers
- Add regression coverage for computed field decorators
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

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

3724-3767: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize decorated computed method/accessor keys before storing them.

Only fields go through OP_TO_PROPERTY_KEY here. For computed methods/getters/setters with decorators, the hidden local keeps the raw expression result, so decorator context.name can be 1 instead of "1" or even the original object instead of the resolved key. Coerce the key before preserving it whenever a later decorator pass reuses that local.

Possible fix
     ACtx.CompileExpression(Elem.ComputedKeyExpression, KeyReg);
+    if (Elem.Kind = cekField) or NeedsKeyLocal then
+      EmitInstruction(ACtx, EncodeABC(OP_TO_PROPERTY_KEY, KeyReg, KeyReg, 0));
 
     case Elem.Kind of
       cekField:
       begin
-        // ES2026 §15.7.10 ClassFieldDefinitionEvaluation evaluates
-        // ClassElementName, including ToPropertyKey, during class definition.
-        EmitInstruction(ACtx, EncodeABC(OP_TO_PROPERTY_KEY, KeyReg, KeyReg, 0));
         Continue;
       end;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Compiler.Statements.pas` around lines 3724 - 3767,
Computed keys for decorated computed methods/getters/setters are not being
coerced to property keys before being stored in the hidden local, so decorator
context.name may see raw non-string values; ensure you run the same
ToPropertyKey conversion used for fields (OP_TO_PROPERTY_KEY) on KeyReg before
writing it to the preserved local when Elem.Decorators is non-empty. Modify the
branch that allocates/declares KeyReg and the code path after
ACtx.CompileExpression(Elem.ComputedKeyExpression, KeyReg) to emit
EncodeABC(OP_TO_PROPERTY_KEY, KeyReg, KeyReg, 0) for any Elem.Kind with
decorators (not just cekField), so the value stored in AComputedFieldKeyLocals
(and used by
CompileComputedGetterBody/CompileComputedSetterBody/CompileComputedMethodBody)
is the normalized property key.
source/units/Goccia.VM.pas (1)

8309-8347: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove SetBytecodeHomeObject call from OP_DEFINE_PROP_DYNAMIC—it should mirror the non-home-object data-property semantics.

OP_DEFINE_DATA_PROP intentionally calls DefineDataPropertyByKeyInternal(..., False) to define plain data properties without setting [[HomeObject]], but OP_DEFINE_PROP_DYNAMIC explicitly calls SetBytecodeHomeObject before property definition. This creates an observable semantic difference where dynamically defined properties receive home-object treatment (changing function semantics) while statically defined data properties do not. Plain data properties should not attach [[HomeObject]]; that is reserved for concise methods via DefineMethodPropertyByKey. Either remove the SetBytecodeHomeObject call or refactor to use DefineDataPropertyByKey consistently.

🤖 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.VM.pas` around lines 8309 - 8347, In
OP_DEFINE_PROP_DYNAMIC remove the call to SetBytecodeHomeObject so dynamic
data-property definition matches OP_DEFINE_DATA_PROP (i.e., do not attach
[[HomeObject]]); specifically, delete the SetBytecodeHomeObject(RightValue,
TargetValue) invocation in the OP_DEFINE_PROP_DYNAMIC branch and ensure the
subsequent property-definition paths use the same data-property APIs
(DefineProperty / DefineSymbolProperty or the existing
DefineDataPropertyByKeyInternal semantics) rather than treating the value as a
method — keep DefineMethodPropertyByKey only for concise method cases.
source/units/Goccia.Evaluator.pas (1)

4455-4472: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Include computed auto-accessors in the resolved-key path.

cekAccessor is skipped here, but the later accessor/decorator code reads ResolvedComputedElementKeys[I] and otherwise falls back to Elem.Name. That leaves accessor [yield expr] outside the computed-name suspend/resume flow and wires computed accessors/decorators against the placeholder name instead of the actual property key. Please resolve/store accessor keys here and carry that key into the auto-accessor registration path too.

Also applies to: 4592-4604

🤖 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 4455 - 4472, The code
currently skips cekAccessor so computed auto-accessor names are never
resolved/stored; update the kind check in the block using Elem.Kind to include
cekAccessor (so it reads like including cekAccessor alongside
cekMethod/getter/setter/field), then perform the same
Continuation.TakeExpressionValue/EvaluateExpression/ToPropertyKey and
Continuation.SaveExpressionValue flow to produce ComputedKey and assign
ResolvedComputedElementKeys[I] for accessor elements as done for other kinds;
also apply the same change to the similar block around the later location (the
4592-4604 area) so the auto-accessor registration path consumes
ResolvedComputedElementKeys[I] instead of falling back to Elem.Name. Ensure you
reference and preserve the existing symbols: Elem.ComputedKeyExpression,
ComputedKey, ToPropertyKey, EvaluateExpression,
Continuation.TakeExpressionValue, Continuation.SaveExpressionValue, and
ResolvedComputedElementKeys.
🤖 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.Evaluator.Decorators.pas`:
- Around line 99-105: The current branches only handle TGocciaObjectValue for
symbol-keyed access (checking FPropertyKey is TGocciaSymbolValue) and return
undefined for non-object receivers; update the dispatch so that when
FPropertyKey is a TGocciaSymbolValue and the receiver (Target) is a
TGocciaClassValue you call TGocciaClassValue(GetSymbolProperty...) to obtain the
symbol property, otherwise preserve the existing TGocciaObjectValue call; apply
the same fix to the analogous get/set branch around the 149-155 region so static
(class) symbol properties are correctly read/written via
TGocciaClassValue.GetSymbolProperty rather than falling through to undefined or
skipping writes.

In `@source/units/Goccia.VM.pas`:
- Around line 5986-6053: DefineDecoratedDataProperty and
DefineDecoratedAccessorProperty currently duplicate computed-key property
installation and bypass the helpers that set bytecode home, causing decorated
methods/getters/setters to have different super resolution; update these
procedures to call the key-aware helpers (DefineMethodPropertyByKey,
DefineGetterPropertyByKey, DefineSetterPropertyByKey) instead of inlining
DefineSymbolProperty/DefineProperty logic, and ensure the helpers (or these call
sites) invoke SetBytecodeHomeObject for function/getter/setter values just like
OP_DEFINE_PROP_DYNAMIC does (and align OP_DEFINE_DATA_PROP semantics
accordingly).

---

Outside diff comments:
In `@source/units/Goccia.Compiler.Statements.pas`:
- Around line 3724-3767: Computed keys for decorated computed
methods/getters/setters are not being coerced to property keys before being
stored in the hidden local, so decorator context.name may see raw non-string
values; ensure you run the same ToPropertyKey conversion used for fields
(OP_TO_PROPERTY_KEY) on KeyReg before writing it to the preserved local when
Elem.Decorators is non-empty. Modify the branch that allocates/declares KeyReg
and the code path after ACtx.CompileExpression(Elem.ComputedKeyExpression,
KeyReg) to emit EncodeABC(OP_TO_PROPERTY_KEY, KeyReg, KeyReg, 0) for any
Elem.Kind with decorators (not just cekField), so the value stored in
AComputedFieldKeyLocals (and used by
CompileComputedGetterBody/CompileComputedSetterBody/CompileComputedMethodBody)
is the normalized property key.

In `@source/units/Goccia.Evaluator.pas`:
- Around line 4455-4472: The code currently skips cekAccessor so computed
auto-accessor names are never resolved/stored; update the kind check in the
block using Elem.Kind to include cekAccessor (so it reads like including
cekAccessor alongside cekMethod/getter/setter/field), then perform the same
Continuation.TakeExpressionValue/EvaluateExpression/ToPropertyKey and
Continuation.SaveExpressionValue flow to produce ComputedKey and assign
ResolvedComputedElementKeys[I] for accessor elements as done for other kinds;
also apply the same change to the similar block around the later location (the
4592-4604 area) so the auto-accessor registration path consumes
ResolvedComputedElementKeys[I] instead of falling back to Elem.Name. Ensure you
reference and preserve the existing symbols: Elem.ComputedKeyExpression,
ComputedKey, ToPropertyKey, EvaluateExpression,
Continuation.TakeExpressionValue, Continuation.SaveExpressionValue, and
ResolvedComputedElementKeys.

In `@source/units/Goccia.VM.pas`:
- Around line 8309-8347: In OP_DEFINE_PROP_DYNAMIC remove the call to
SetBytecodeHomeObject so dynamic data-property definition matches
OP_DEFINE_DATA_PROP (i.e., do not attach [[HomeObject]]); specifically, delete
the SetBytecodeHomeObject(RightValue, TargetValue) invocation in the
OP_DEFINE_PROP_DYNAMIC branch and ensure the subsequent property-definition
paths use the same data-property APIs (DefineProperty / DefineSymbolProperty or
the existing DefineDataPropertyByKeyInternal semantics) rather than treating the
value as a method — keep DefineMethodPropertyByKey only for concise method
cases.
🪄 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: 0bc6e272-b500-4052-90c0-04221e6e4470

📥 Commits

Reviewing files that changed from the base of the PR and between 92b4b9e and 3165c91.

📒 Files selected for processing (5)
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Evaluator.Decorators.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.VM.pas
  • tests/language/decorators/computed-field-decorator.js

Comment thread source/units/Goccia.Evaluator.Decorators.pas
Comment thread source/units/Goccia.VM.pas Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

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

4463-4470: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the completed-expression cache for resolved computed keys.

Line 4464/4470 stores a fully resolved computed key in the partial-expression channel, but that channel is also used for in-progress state on the same AST node. For a key like [a + (yield 1)], EvaluateBinary can resume with the saved left operand, and this code will incorrectly treat that partial value as the final property key. The replay and teardown here should use the completed-expression channel instead of TakeExpressionValue / SaveExpressionValue / ClearExpressionValue.

Based on learnings: GocciaScript keeps partial-expression and completed-expression continuation caches separate; conflating them breaks cases like a + (yield b) on resume.

Also applies to: 5106-5111

🤖 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 4463 - 4470, The code is
saving/reading a fully resolved computed key into the partial-expression
continuation channel (using Continuation.TakeExpressionValue /
SaveExpressionValue / ClearExpressionValue), which conflates in-progress state
with completed values; change these to use the completed-expression continuation
cache APIs (use the corresponding TakeCompletedExpressionValue /
SaveCompletedExpressionValue / ClearCompletedExpressionValue or whatever the
completed-expression equivalents are) when handling Elem.ComputedKeyExpression
so that ToPropertyKey(EvaluateExpression(...)) reads/writes the
completed-expression slot; apply the same replacement for the other occurrence
around lines 5106-5111.
🤖 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.

Outside diff comments:
In `@source/units/Goccia.Evaluator.pas`:
- Around line 4463-4470: The code is saving/reading a fully resolved computed
key into the partial-expression continuation channel (using
Continuation.TakeExpressionValue / SaveExpressionValue / ClearExpressionValue),
which conflates in-progress state with completed values; change these to use the
completed-expression continuation cache APIs (use the corresponding
TakeCompletedExpressionValue / SaveCompletedExpressionValue /
ClearCompletedExpressionValue or whatever the completed-expression equivalents
are) when handling Elem.ComputedKeyExpression so that
ToPropertyKey(EvaluateExpression(...)) reads/writes the completed-expression
slot; apply the same replacement for the other occurrence around lines
5106-5111.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4d33e1ff-ee86-46f6-8c8c-ec1ec3fddf7a

📥 Commits

Reviewing files that changed from the base of the PR and between 3165c91 and 027c014.

📒 Files selected for processing (11)
  • source/units/Goccia.Bytecode.OpCodeNames.pas
  • source/units/Goccia.Bytecode.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Evaluator.Decorators.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.VM.pas
  • source/units/Goccia.Values.ClassValue.pas
  • tests/language/decorators/auto-accessor-decorator.js
  • tests/language/decorators/auto-accessor.js
  • tests/language/decorators/computed-field-decorator.js

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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.Values.ClassValue.pas (1)

722-729: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Static decorator field initializers are enqueued but never executed.

Lines 851–852 show RunDecoratorFieldInitializers explicitly skips entries where IsStatic=True. However, call sites in Goccia.Evaluator.pas (lines 4976, 5021) unconditionally pass Elem.IsStatic to AddFieldInitializerWithKey, meaning static decorated fields are being queued. The class provides no corresponding static execution method, so these initializers will never run.

🤖 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.Values.ClassValue.pas` around lines 722 - 729,
TGocciaClassValue currently queues decorator field initializers
(AddFieldInitializerWithKey) even when AIsStatic=True but
RunDecoratorFieldInitializers skips static entries, so static initializers never
run; fix by either (A) filtering static entries out in
AddFieldInitializerWithKey (return immediately when AIsStatic is true) so only
instance initializers are queued, or (B) support static initializers properly by
adding a dedicated storage (e.g., FStaticDecoratorFieldInitializers), a new
runner method RunDecoratorStaticFieldInitializers, and ensure call sites (those
in Goccia.Evaluator.pas that pass Elem.IsStatic) trigger that runner for static
fields; update references to TGocciaClassValue.AddFieldInitializerWithKey and
RunDecoratorFieldInitializers accordingly.
source/units/Goccia.VM.pas (1)

6004-6066: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep decorated class elements non-enumerable.

These helpers now route decorated class methods/getters/setters through the generic *ByKey definers, but those install [pfEnumerable] descriptors. That changes Object.keys/for...in behavior for decorated class elements and diverges from the non-decorated class method path in this file. Preserve the computed-key/home-object logic here, but use class-element descriptor flags instead ([pfConfigurable, pfWritable] for methods and [pfConfigurable] for accessors).

🤖 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.VM.pas` around lines 6004 - 6066, The decorated-element
helpers currently call the generic "*ByKey" definers which set pfEnumerable;
update DefineDecoratedMethodProperty, DefineDecoratedGetterProperty and
DefineDecoratedSetterProperty so decorated class elements are non-enumerable by
using the class-element descriptor flags: for methods set [pfConfigurable,
pfWritable] and for accessors set [pfConfigurable]. Concretely, change the calls
inside DefineDecoratedMethodProperty (currently calling
DefineMethodPropertyByKey(TargetValue, ValueToRegister(KeyValue), AValue)) to
the variant that accepts/sets descriptor flags (or add a flag parameter) so the
created descriptor uses pfConfigurable+pfWritable; likewise change
DefineDecoratedGetterProperty (currently calling
DefineStaticGetterPropertyByKey/DefineGetterPropertyByKey) and
DefineDecoratedSetterProperty (currently calling
DefineStaticSetterPropertyByKey/DefineSetterPropertyByKey) to use the accessor
descriptor flag pfConfigurable and ensure the key handling
(ValueToRegister/creating TGocciaStringLiteralValue) and class vs prototype
target logic remain unchanged.
🤖 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.Evaluator.Decorators.pas`:
- Around line 153-165: The branch that handles non-symbol FPropertyKey currently
calls TGocciaObjectValue.AssignProperty and thus bypasses class-specific logic;
change it so that when Target is TGocciaClassValue you call
TGocciaClassValue(Target).SetProperty(FPropertyName, NewValue) (or the
appropriate SetProperty signature) instead of TGocciaObjectValue.AssignProperty,
while keeping the existing AssignSymbolProperty and AssignProperty calls for the
other cases (symbols and plain objects); update the logic around Target,
FPropertyKey, FPropertyName, TGocciaClassValue and TGocciaObjectValue to ensure
class writes go through TGocciaClassValue.SetProperty.

---

Outside diff comments:
In `@source/units/Goccia.Values.ClassValue.pas`:
- Around line 722-729: TGocciaClassValue currently queues decorator field
initializers (AddFieldInitializerWithKey) even when AIsStatic=True but
RunDecoratorFieldInitializers skips static entries, so static initializers never
run; fix by either (A) filtering static entries out in
AddFieldInitializerWithKey (return immediately when AIsStatic is true) so only
instance initializers are queued, or (B) support static initializers properly by
adding a dedicated storage (e.g., FStaticDecoratorFieldInitializers), a new
runner method RunDecoratorStaticFieldInitializers, and ensure call sites (those
in Goccia.Evaluator.pas that pass Elem.IsStatic) trigger that runner for static
fields; update references to TGocciaClassValue.AddFieldInitializerWithKey and
RunDecoratorFieldInitializers accordingly.

In `@source/units/Goccia.VM.pas`:
- Around line 6004-6066: The decorated-element helpers currently call the
generic "*ByKey" definers which set pfEnumerable; update
DefineDecoratedMethodProperty, DefineDecoratedGetterProperty and
DefineDecoratedSetterProperty so decorated class elements are non-enumerable by
using the class-element descriptor flags: for methods set [pfConfigurable,
pfWritable] and for accessors set [pfConfigurable]. Concretely, change the calls
inside DefineDecoratedMethodProperty (currently calling
DefineMethodPropertyByKey(TargetValue, ValueToRegister(KeyValue), AValue)) to
the variant that accepts/sets descriptor flags (or add a flag parameter) so the
created descriptor uses pfConfigurable+pfWritable; likewise change
DefineDecoratedGetterProperty (currently calling
DefineStaticGetterPropertyByKey/DefineGetterPropertyByKey) and
DefineDecoratedSetterProperty (currently calling
DefineStaticSetterPropertyByKey/DefineSetterPropertyByKey) to use the accessor
descriptor flag pfConfigurable and ensure the key handling
(ValueToRegister/creating TGocciaStringLiteralValue) and class vs prototype
target logic remain unchanged.
🪄 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: 417ae3f2-912b-4dd2-99ee-5a9beae656de

📥 Commits

Reviewing files that changed from the base of the PR and between 3165c91 and 185d265.

📒 Files selected for processing (12)
  • source/units/Goccia.Bytecode.OpCodeNames.pas
  • source/units/Goccia.Bytecode.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Evaluator.Decorators.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.Generator.Continuation.pas
  • source/units/Goccia.Parser.pas
  • source/units/Goccia.VM.pas
  • source/units/Goccia.Values.ClassValue.pas
  • tests/language/decorators/auto-accessor-decorator.js
  • tests/language/decorators/auto-accessor.js
  • tests/language/decorators/computed-field-decorator.js

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

Caution

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

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

5069-5074: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Run static field decorator initializers before a class decorator can swap out ClassValue.

By Line 5073, ClassValue may already have been replaced by a class decorator, but the static field/accessor decorator initializers were registered on the original class during element decoration. In cases where a class decorator returns a replacement constructor, this call will run those initializers on the wrong object or skip them entirely.

💡 Suggested direction
-    // Store initializer lists on the class for execution during instantiation
+    // Store initializer lists on the class for execution during instantiation
     InitializerResults := MethodCollector.GetInitializers;
     ClassValue.SetMethodInitializers(InitializerResults);
     InitializerResults := FieldCollector.GetInitializers;
     ClassValue.SetFieldInitializers(InitializerResults);
-    ClassValue.RunDecoratorStaticFieldInitializers;
 
     // Run class-level initializers after static fields
     InitializerResults := ClassCollector.GetInitializers;

Capture the pre-class-decorator class value and run RunDecoratorStaticFieldInitializers on that original class before ClassValue := TGocciaClassValue(DecoratorResult) can redirect the reference, or explicitly transfer the pending static initializer state when a class decorator replaces the class.

🤖 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 5069 - 5074, The static
field/accessor decorator initializers must be executed on the original class
object before a class decorator can replace ClassValue; capture the original
class instance (the value currently referenced by ClassValue) prior to calling
MethodCollector.GetInitializers / FieldCollector.GetInitializers and then invoke
RunDecoratorStaticFieldInitializers on that captured instance (or transfer the
pending initializer state to the replacement) before assigning ClassValue :=
TGocciaClassValue(DecoratorResult) or any code that applies the class decorator
result, ensuring initializers registered during element decoration run on the
original constructor.
♻️ Duplicate comments (1)
source/units/Goccia.VM.pas (1)

6007-6131: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve the class-value path for decorated static members.

Lines 6011, 6059, and 6101 downcast ClassVal to TGocciaObjectValue and then inline the static definition logic. That means the subsequent SetBytecodeHomeObject(...) calls no longer see the static-class path, so decorated static methods/getters/setters can resolve super against the wrong chain. The inline accessor merge also bypasses GetOwnStaticSymbolDescriptor(...) for symbol-keyed static accessors. Please route the static cases back through the existing static key-aware helpers, or at least pass ClassVal into SetBytecodeHomeObject and keep the class-specific static-symbol descriptor lookup. Based on learnings: static/computed definition sites in source/units/Goccia.VM.pas must pass the class value into SetBytecodeHomeObject so super resolves on the correct prototype chain.

🤖 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.VM.pas` around lines 6007 - 6131, Static decorated
definitions cast ClassVal to TGocciaObjectValue which loses the class-value path
causing SetBytecodeHomeObject to resolve super against the wrong prototype and
also bypasses static-symbol descriptor helpers; update the static branches in
the routines that define decorators (the block that defines properties,
DefineDecoratedGetterProperty and DefineDecoratedSetterProperty) so that when
AIsStatic is true you: pass the original ClassVal (as
TGocciaClassValue(ClassVal)) into SetBytecodeHomeObject instead of the
downcasted TGocciaObjectValue, and when reading existing descriptors for symbol
keys call the class-specific static helper (GetOwnStaticSymbolDescriptor) rather
than the instance symbol lookup; alternatively refactor to route static cases
through the existing static key-aware helpers so static symbol/property lookups
and SetBytecodeHomeObject preserve the class value path.
🤖 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.

Outside diff comments:
In `@source/units/Goccia.Evaluator.pas`:
- Around line 5069-5074: The static field/accessor decorator initializers must
be executed on the original class object before a class decorator can replace
ClassValue; capture the original class instance (the value currently referenced
by ClassValue) prior to calling MethodCollector.GetInitializers /
FieldCollector.GetInitializers and then invoke
RunDecoratorStaticFieldInitializers on that captured instance (or transfer the
pending initializer state to the replacement) before assigning ClassValue :=
TGocciaClassValue(DecoratorResult) or any code that applies the class decorator
result, ensuring initializers registered during element decoration run on the
original constructor.

---

Duplicate comments:
In `@source/units/Goccia.VM.pas`:
- Around line 6007-6131: Static decorated definitions cast ClassVal to
TGocciaObjectValue which loses the class-value path causing
SetBytecodeHomeObject to resolve super against the wrong prototype and also
bypasses static-symbol descriptor helpers; update the static branches in the
routines that define decorators (the block that defines properties,
DefineDecoratedGetterProperty and DefineDecoratedSetterProperty) so that when
AIsStatic is true you: pass the original ClassVal (as
TGocciaClassValue(ClassVal)) into SetBytecodeHomeObject instead of the
downcasted TGocciaObjectValue, and when reading existing descriptors for symbol
keys call the class-specific static helper (GetOwnStaticSymbolDescriptor) rather
than the instance symbol lookup; alternatively refactor to route static cases
through the existing static key-aware helpers so static symbol/property lookups
and SetBytecodeHomeObject preserve the class value path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aec1c221-3f18-4202-b3e1-e1b33791697f

📥 Commits

Reviewing files that changed from the base of the PR and between 185d265 and 4e7c94f.

📒 Files selected for processing (5)
  • source/units/Goccia.Evaluator.Decorators.pas
  • source/units/Goccia.Evaluator.pas
  • source/units/Goccia.VM.pas
  • source/units/Goccia.Values.ClassValue.pas
  • tests/language/decorators/computed-field-decorator.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working internal Refactoring, CI, tooling, cleanup spec compliance Mismatch against official JavaScript/TypeScript specification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Evaluate yield in computed public class field names

1 participant