Skip to content

Thread optional receiver through Reflect.get and Reflect.set#243

Merged
frostney merged 4 commits into
mainfrom
fix/reflect-get-set-receiver
Apr 9, 2026
Merged

Thread optional receiver through Reflect.get and Reflect.set#243
frostney merged 4 commits into
mainfrom
fix/reflect-get-set-receiver

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 9, 2026

Summary

  • Reflect.get: passes the optional receiver argument to getter calls via GetPropertyWithContext (string keys) and GetSymbolPropertyWithReceiver (symbol keys), so accessor getters observe the correct this (ES2026 §28.1.5)
  • Reflect.set: adds AssignPropertyWithReceiver / AssignSymbolPropertyWithReceiver implementing the full OrdinarySetWithOwnDescriptor algorithm (ES2026 §10.1.9.2) — accessor setters receive receiver as this, and data property writes land on receiver rather than target
  • Adds 16 new tests covering receiver threading for getters, setters, inherited accessors, symbol keys, data-property-on-receiver writes, and edge cases (non-writable, accessor conflicts, non-extensible receiver)

Closes #230

Test plan

  • All 86 Reflect tests pass (70 existing + 16 new)
  • Full test suite passes (4027 tests, 0 new failures)
  • Object.defineProperty tests pass (191 tests)
  • Formatter check clean

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

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: e51e9693-1911-40af-876d-8915d6417c5e

📥 Commits

Reviewing files that changed from the base of the PR and between 3417e20 and 8bd204b.

📒 Files selected for processing (1)
  • units/Goccia.Values.ProxyValue.pas

📝 Walkthrough

Walkthrough

Threads the optional receiver through Reflect.get and Reflect.set and the object property pipeline: Reflect defaults receiver to target when omitted, calls context-aware getters/setters with receiver as this, and applies data writes to the receiver per ECMAScript semantics.

Changes

Cohort / File(s) Summary
Tests: Reflect.get
tests/built-ins/Reflect/get.js
Added tests verifying Reflect.get receiver behavior: defaulting to target, getter this binding using receiver, prototype-chain and symbol-keyed accessor handling.
Tests: Reflect.set
tests/built-ins/Reflect/set.js
Added tests exercising Reflect.set receiver semantics: setter this binding (including symbol-keyed and inherited), writes landing on receiver, various success/failure cases, and receiver defaulting.
Reflect builtin
units/Goccia.Builtins.GlobalReflect.pas
Extract optional receiver arg (default target) in ReflectGet/ReflectSet; call receiver-aware get/set helpers and switch to boolean-returning assign helpers for Reflect.set.
Core object APIs
units/Goccia.Values.ObjectValue.pas
Added AssignPropertyWithReceiver / AssignSymbolPropertyWithReceiver (return Boolean); implemented ES OrdinarySetWithOwnDescriptor semantics; made context-aware getters/setters virtual.
Proxy value
units/Goccia.Values.ProxyValue.pas
Added receiver-aware GetPropertyWithContext / AssignPropertyWithReceiver and symbol variants; pass receiver into traps and delegate to target with receiver when forwarding.
Per-value GetPropertyWithContext overrides
units/Goccia.Values.*.pas
...ArrayBufferValue.pas, ...ArrayValue.pas, ...BooleanObjectValue.pas, ...ClassValue.pas, ...FFILibrary.pas, ...FFIPointer.pas, ...FunctionBase.pas, ...MapValue.pas, ...NumberObjectValue.pas, ...PromiseValue.pas, ...SetValue.pas, ...SharedArrayBufferValue.pas, ...StringObjectValue.pas, ...ArrayValue.pas
Introduced GetPropertyWithContext (receiver/context) overrides and changed GetProperty to delegate to GetPropertyWithContext(AName, Self) so property lookup, prototype fallback, and accessor invocation propagate the provided receiver/context.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Caller
    participant ReflectGet as Reflect.get
    participant Target as Target
    participant Proto as Prototype
    participant Getter as Accessor Getter

    Caller->>ReflectGet: Reflect.get(target, prop, receiver?)
    ReflectGet->>ReflectGet: Receiver := receiver ?? target
    ReflectGet->>Target: GetPropertyWithContext(prop, Receiver)
    alt Own data property
        Target-->>ReflectGet: value
    else Own accessor
        Target->>Getter: Call getter with Receiver as this
        Getter-->>Target: value
        Target-->>ReflectGet: value
    else Not own → Proto
        Target->>Proto: GetPropertyWithContext(prop, Receiver)
        alt Accessor found
            Proto->>Getter: Call getter with Receiver as this
            Getter-->>Proto: value
            Proto-->>Target: value
            Target-->>ReflectGet: value
        else Data found
            Proto-->>Target: value
            Target-->>ReflectGet: value
        end
    end
    ReflectGet-->>Caller: value
Loading
sequenceDiagram
    participant Caller as Caller
    participant ReflectSet as Reflect.set
    participant Target as Target
    participant Proto as Prototype
    participant Setter as Accessor Setter
    participant ReceiverObj as Receiver

    Caller->>ReflectSet: Reflect.set(target, prop, value, receiver?)
    ReflectSet->>ReflectSet: Receiver := receiver ?? target
    ReflectSet->>Target: AssignPropertyWithReceiver(prop, value, Receiver)
    alt Own data descriptor on Target
        alt writable
            Target->>ReceiverObj: Define/Update data property on Receiver
            ReceiverObj-->>Target: success (true)
            Target-->>ReflectSet: true
        else not writable
            Target-->>ReflectSet: false
        end
    else Not own → find on Proto
        alt Accessor with setter found
            Proto->>Setter: Call setter(value) with Receiver as this
            Setter-->>Proto: success
            Proto-->>Target: true
            Target-->>ReflectSet: true
        else Not found and Receiver extensible
            Target->>ReceiverObj: Define new data property on Receiver
            ReceiverObj-->>Target: success
            Target-->>ReflectSet: true
        else Not found and Receiver non-extensible
            Target-->>ReflectSet: false
        end
    end
    ReflectSet-->>Caller: boolean
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 'Thread optional receiver through Reflect.get and Reflect.set' is clear and specific, directly describing the main change: threading the receiver parameter through these two Reflect methods.
Description check ✅ Passed The description includes a clear summary of the changes to both Reflect.get and Reflect.set, references the linked issue (#230), mentions test coverage (16 new tests), and reports passing test results. All key sections are present and substantive.
Linked Issues check ✅ Passed The PR fully addresses issue #230: Reflect.get now threads receiver through getter calls via GetPropertyWithContext/GetSymbolPropertyWithReceiver [#230]; Reflect.set implements OrdinarySetWithOwnDescriptor with receiver-aware assignment for both accessors and data properties [#230]; receiver defaults to target when omitted [#230].
Out of Scope Changes check ✅ Passed All changes directly support the receiver-threading objective: test additions for Reflect.get/set, implementation of receiver-aware methods across value types, and proxy overrides for receiver-aware routing. No unrelated refactoring or incidental changes detected.
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.

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)
units/Goccia.Values.ObjectValue.pas (1)

53-77: ⚠️ Potential issue | 🔴 Critical

Make the receiver-aware property hooks virtual before routing Reflect through them.

Reflect.get/Reflect.set now depend on GetPropertyWithContext, GetSymbolPropertyWithReceiver, AssignPropertyWithReceiver, and AssignSymbolPropertyWithReceiver, but this API still lives as concrete TGocciaObjectValue methods. That means subtype-specific property behavior cannot participate in the receiver-aware path, so cases like Reflect.get(arr, "length", receiver) or Reflect.set(arr, "length", 1) can silently fall back to plain-object semantics instead of the subclass’s [[Get]] / [[Set]].

Suggested API direction
-    function AssignPropertyWithReceiver(const AName: string; const AValue: TGocciaValue; const AReceiver: TGocciaValue): Boolean;
+    function AssignPropertyWithReceiver(const AName: string; const AValue: TGocciaValue; const AReceiver: TGocciaValue): Boolean; virtual;

-    function GetPropertyWithContext(const AName: string; const AThisContext: TGocciaValue): TGocciaValue;
+    function GetPropertyWithContext(const AName: string; const AThisContext: TGocciaValue): TGocciaValue; virtual;

-    function AssignSymbolPropertyWithReceiver(const ASymbol: TGocciaSymbolValue; const AValue: TGocciaValue; const AReceiver: TGocciaValue): Boolean;
+    function AssignSymbolPropertyWithReceiver(const ASymbol: TGocciaSymbolValue; const AValue: TGocciaValue; const AReceiver: TGocciaValue): Boolean; virtual;

-    function GetSymbolPropertyWithReceiver(const ASymbol: TGocciaSymbolValue; const AReceiver: TGocciaValue): TGocciaValue;
+    function GetSymbolPropertyWithReceiver(const ASymbol: TGocciaSymbolValue; const AReceiver: TGocciaValue): TGocciaValue; virtual;

Subclasses that already customize property access would then need receiver-aware overrides too.

As per coding guidelines, "Use virtual methods on TGocciaValue for polymorphic operations: GetProperty(Name), SetProperty(Name, Value) for property access".

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

In `@units/Goccia.Values.ObjectValue.pas` around lines 53 - 77, The receiver-aware
hooks (AssignPropertyWithReceiver, AssignSymbolPropertyWithReceiver,
GetPropertyWithContext, GetSymbolPropertyWithReceiver) are currently concrete on
TGocciaObjectValue so subclasses cannot customize receiver-aware semantics;
change their declarations to be virtual (ideally declared on TGocciaValue or at
least marked virtual in TGocciaObjectValue) and keep/adjust existing overrides
in subclasses so Reflect.get/Reflect.set routing uses polymorphic overrides;
update method signatures for AssignPropertyWithReceiver,
AssignSymbolPropertyWithReceiver, GetPropertyWithContext,
GetSymbolPropertyWithReceiver to virtual and ensure any subclass implementations
use override.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@units/Goccia.Values.ObjectValue.pas`:
- Around line 53-77: The receiver-aware hooks (AssignPropertyWithReceiver,
AssignSymbolPropertyWithReceiver, GetPropertyWithContext,
GetSymbolPropertyWithReceiver) are currently concrete on TGocciaObjectValue so
subclasses cannot customize receiver-aware semantics; change their declarations
to be virtual (ideally declared on TGocciaValue or at least marked virtual in
TGocciaObjectValue) and keep/adjust existing overrides in subclasses so
Reflect.get/Reflect.set routing uses polymorphic overrides; update method
signatures for AssignPropertyWithReceiver, AssignSymbolPropertyWithReceiver,
GetPropertyWithContext, GetSymbolPropertyWithReceiver to virtual and ensure any
subclass implementations use override.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4d973150-75ad-4ce0-a20f-a8cd1092d1c3

📥 Commits

Reviewing files that changed from the base of the PR and between a85b773 and 955414d.

📒 Files selected for processing (4)
  • tests/built-ins/Reflect/get.js
  • tests/built-ins/Reflect/set.js
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Values.ObjectValue.pas

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 229 improved · 🔴 9 regressed · 36 unchanged · avg +7.5%
Bytecode: 🟢 62 improved · 🔴 86 regressed · 126 unchanged · avg -0.3%

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

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 4062 4062
Tests Passed 4021 ✅ 4062 ✅
Tests Skipped 41 0
Tests Test Duration 286.5ms 273.3ms
Tests Lex 79.1ms 53.1ms
Tests Parse 99.7ms 101.3ms
Tests Compile 65.1ms
Tests Execute 302.3ms 304.4ms
Tests Engine Total 481.1ms 523.9ms
Benchmarks Total 274 274
Benchmarks Duration 7.67min 6.43min

Measured on ubuntu-latest x64.

frostney and others added 2 commits April 9, 2026 12:19
Reflect.get now passes the receiver to getter calls via GetPropertyWithContext
(string keys) and GetSymbolPropertyWithReceiver (symbol keys). Reflect.set uses
new AssignPropertyWithReceiver / AssignSymbolPropertyWithReceiver methods that
implement the ES2026 §10.1.9.2 OrdinarySetWithOwnDescriptor algorithm: accessor
setters receive the receiver as this, and data property writes land on the
receiver rather than the target.

Fixes #230

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mark GetPropertyWithContext, GetSymbolPropertyWithReceiver,
AssignPropertyWithReceiver, and AssignSymbolPropertyWithReceiver as
virtual on TGocciaObjectValue so subclasses can customize
receiver-aware semantics.

Add GetPropertyWithContext overrides to all 14 subclasses that
previously overrided GetProperty: each override moves the custom
property logic (length, size, byteLength, indices, accessors, etc.)
into GetPropertyWithContext and simplifies GetProperty to delegate
via GetPropertyWithContext(AName, Self). This ensures Reflect.get
routes through polymorphic dispatch for both default and explicit
receiver cases.

Remove the args-length workaround in ReflectGet — now always uses
GetPropertyWithContext since virtual dispatch handles subclasses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney force-pushed the fix/reflect-get-set-receiver branch from 955414d to e571e40 Compare April 9, 2026 11:29
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
units/Goccia.Values.ProxyValue.pas (1)

198-229: ⚠️ Potential issue | 🔴 Critical

Receiver is still dropped in proxy [[Get]] path.

In GetPropertyWithContext, Line 202 passes Self to the get trap, and Line 228 falls back to FTarget.GetProperty(AName). Both should use AThisContext; otherwise Reflect.get(proxy, key, receiver) observes the wrong this.

Suggested fix
@@
-      Args.Add(Self);
+      Args.Add(AThisContext);
@@
-  else
-    Result := FTarget.GetProperty(AName);
+  else
+    Result := FTarget.GetPropertyWithContext(AName, AThisContext);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Values.ProxyValue.pas` around lines 198 - 229,
GetPropertyWithContext drops the receiver: it passes Self to InvokeTrap and
falls back to FTarget.GetProperty(AName) instead of using the provided receiver;
update the calls in GetPropertyWithContext so InvokeTrap is given AThisContext
(not Self) as the third arg and the fallback uses FTarget.GetProperty(AName,
AThisContext) (or the equivalent overload) so Reflect.get(proxy, key, receiver)
and Trap invocation observe the correct receiver; locate these changes around
the InvokeTrap call and the FTarget.GetProperty fallback in
GetPropertyWithContext.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@units/Goccia.Values.ObjectValue.pas`:
- Around line 599-612: When OwnDesc = nil in AssignPropertyWithReceiver (and
similarly in AssignSymbolPropertyWithReceiver) you must first consult the
receiver's own property before creating a new one: if AReceiver is a
TGocciaObjectValue, call its GetOwnProperty/GetOwnPropertyDescriptor (or
equivalent) to obtain ReceiverDesc; if ReceiverDesc is nil then proceed to
DefineProperty only when ReceiverObj.Extensible is true, but if ReceiverDesc
exists then obey its attributes—update value if it's a writable data property,
return False if it's a non-writable data property, and return False for accessor
properties without a setter—do not unconditionally call DefineProperty; ensure
you reference FPrototype and existing logic for prototype fallthrough and keep
use of TGocciaPropertyDescriptorData and DefineProperty/DefineSymbolProperty
unchanged except guarded by the receiver-own-property checks.

---

Outside diff comments:
In `@units/Goccia.Values.ProxyValue.pas`:
- Around line 198-229: GetPropertyWithContext drops the receiver: it passes Self
to InvokeTrap and falls back to FTarget.GetProperty(AName) instead of using the
provided receiver; update the calls in GetPropertyWithContext so InvokeTrap is
given AThisContext (not Self) as the third arg and the fallback uses
FTarget.GetProperty(AName, AThisContext) (or the equivalent overload) so
Reflect.get(proxy, key, receiver) and Trap invocation observe the correct
receiver; locate these changes around the InvokeTrap call and the
FTarget.GetProperty fallback in GetPropertyWithContext.
🪄 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: 97733d74-d789-4921-92d8-2780ec51fb3e

📥 Commits

Reviewing files that changed from the base of the PR and between 955414d and e571e40.

📒 Files selected for processing (18)
  • tests/built-ins/Reflect/get.js
  • tests/built-ins/Reflect/set.js
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.BooleanObjectValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.SetValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.StringObjectValue.pas
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/built-ins/Reflect/get.js

Comment thread units/Goccia.Values.ObjectValue.pas
Proxy GetPropertyWithContext: pass AThisContext (not Self) as the
receiver argument to the get trap, and thread the receiver through
the no-trap fallback via target.GetPropertyWithContext. This ensures
Reflect.get(proxy, key, receiver) observes the correct this in traps.

AssignPropertyWithReceiver / AssignSymbolPropertyWithReceiver: when
the prototype chain ends (OwnDesc = nil, no prototype), consult
Receiver.[[GetOwnProperty]](P) before creating a new property per
ES2026 §10.1.9.2 step 1c→2. This correctly returns false when the
receiver already has a non-writable data property or an accessor for
the same key, and updates an existing writable property in place.

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@units/Goccia.Values.ObjectValue.pas`:
- Around line 609-623: The receiver update must go through the boolean
[[DefineOwnProperty]] path instead of mutating descriptors or directly calling
DefineProperty; replace in-place writes (the check for Assigned(ReceiverDesc),
the Writable test, and the assignment TGocciaPropertyDescriptorData.Value :=
AValue) with logic that constructs a new property descriptor reflecting the
desired change and calls ReceiverObj.DefineOwnProperty(AName, NewDesc) (or the
symbol variant) returning its boolean result; likewise, when creating a new
property, check ReceiverObj.Extensible and then call
ReceiverObj.DefineOwnProperty(AName, NewDataDesc) instead of
DefineProperty/DefineSymbolProperty so the proxy
DefineOwnProperty/defineProperty trap behavior and false return/throw semantics
are preserved (apply same fix to the other affected blocks around the
identifiers ReceiverObj.GetOwnPropertyDescriptor, TGocciaPropertyDescriptorData,
ReceiverObj.DefineProperty/DefineSymbolProperty mentioned in the comment).

In `@units/Goccia.Values.ProxyValue.pas`:
- Around line 30-31: TGocciaProxyValue currently overrides
GetPropertyWithContext but misses overrides for the receiver-aware virtuals, so
symbol-key reads and receiver-based writes bypass proxy traps; update
TGocciaProxyValue to also override GetSymbolPropertyWithReceiver,
AssignPropertyWithReceiver, and AssignSymbolPropertyWithReceiver on the
TGocciaValue hierarchy, delegating to the proxy trap logic (or invoking the same
proxy-handling code used by GetPropertyWithContext) and preserving invariant
checks for symbol keys and receiver-aware assignments; ensure each new override
calls the proxy target/trap handling paths rather than the default TGocciaValue
implementations so Reflect.get/set semantics remain correct.
🪄 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: 71c2abcb-dd03-4577-9631-d629383edb26

📥 Commits

Reviewing files that changed from the base of the PR and between e571e40 and 3417e20.

📒 Files selected for processing (2)
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.ProxyValue.pas

Comment thread units/Goccia.Values.ObjectValue.pas
Comment thread units/Goccia.Values.ProxyValue.pas
TGocciaProxyValue now overrides GetSymbolPropertyWithReceiver,
AssignPropertyWithReceiver, and AssignSymbolPropertyWithReceiver so
that Reflect.get/set with symbol keys and explicit receivers route
through proxy traps instead of bypassing them via the base
TGocciaObjectValue implementation.

AssignPropertyWithReceiver returns false (not throw) when the set
trap returns a falsy value, matching the boolean OrdinarySet contract.
All three overrides include the same invariant validation as the
existing string-key counterparts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney merged commit ad97ce9 into main Apr 9, 2026
9 checks passed
@frostney frostney deleted the fix/reflect-get-set-receiver branch April 9, 2026 12:17
@frostney frostney added the bug Something isn't working label Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reflect.get / Reflect.set: thread optional receiver parameter through property access pipeline

1 participant