Skip to content

Extract shared ToPropertyDescriptor helper#239

Merged
frostney merged 3 commits into
mainfrom
refactor/shared-to-property-descriptor
Apr 9, 2026
Merged

Extract shared ToPropertyDescriptor helper#239
frostney merged 3 commits into
mainfrom
refactor/shared-to-property-descriptor

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 9, 2026

Summary

  • Extracts a shared ToPropertyDescriptor function (units/Goccia.PropertyDescriptor.Utils.pas) implementing ES2026 §6.2.5.5
  • Replaces ~75 lines of duplicated descriptor-parsing logic in both Object.defineProperty and Reflect.defineProperty with a single call to the shared helper
  • Fixes a behavioral inconsistency: Object.defineProperty was missing the mixed data+accessor rejection check (ES2026 §6.2.5.5 step 10) that Reflect.defineProperty had — both APIs now throw TypeError for descriptors with both (value/writable) and (get/set)
  • Unifies getter/setter callable validation to use IsCallable VMT dispatch (Reflect previously used is TGocciaFunctionBase)

Test plan

  • All 15 existing Object.defineProperty and Reflect.defineProperty tests pass
  • Added 4 new tests covering mixed descriptor rejection and non-callable getter/setter validation for both APIs
  • Full test suite passes (2763/2764, the 1 skip is the pre-existing ASI test that requires --asi)
  • Formatter check passes

Closes #236

🤖 Generated with Claude Code

…roperty

Unify descriptor-parsing logic that was duplicated between Object.defineProperty
and Reflect.defineProperty into a single ToPropertyDescriptor function
(ES2026 §6.2.5.5), ensuring both APIs share identical validation semantics
including mixed data+accessor rejection and getter/setter callable checks.

Closes #236

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@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: 7f0b9915-66f0-4e91-982d-125910846bb2

📥 Commits

Reviewing files that changed from the base of the PR and between 4ae9890 and 0c9ec2b.

📒 Files selected for processing (1)
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
🚧 Files skipped from review as they are similar to previous changes (1)
  • units/Goccia.Values.ObjectPropertyDescriptor.pas

📝 Walkthrough

Walkthrough

Extracted descriptor-parsing logic from Object.defineProperty and Reflect.defineProperty into a shared ToPropertyDescriptor(DescriptorObject, ExistingDescriptor) helper; both builtins now delegate descriptor conversion and validation to this helper which enforces callable checks, inheritance of unspecified attributes, and rejection of mixed data+accessor descriptors.

Changes

Cohort / File(s) Summary
Documentation
docs/value-system.md
Added ToPropertyDescriptor subsection describing the new helper, its ES2026 reference, and enforced semantics (inheritance from existing descriptor, callable/undefined get/set, reject mixed data+accessor descriptors).
Tests
tests/built-ins/Object/defineProperty.js, tests/built-ins/Reflect/defineProperty.js
Added tests asserting TypeError for mixed data+accessor descriptors and for non-callable non-undefined get/set values; also verify get: undefined / set: undefined are accepted when appropriate.
New Helper
units/Goccia.Values.ObjectPropertyDescriptor.pas
Added exported ToPropertyDescriptor(const ADescriptorObject, AExistingDescriptor): TGocciaPropertyDescriptor that validates descriptor object type, inherits unspecified attributes from an existing descriptor, validates get/set as callable or undefined, rejects mixed descriptors, and returns the correct descriptor subtype with proper flags.
Builtin Refactors
units/Goccia.Builtins.GlobalObject.pas, units/Goccia.Builtins.GlobalReflect.pas
Removed inline descriptor-parsing and validation logic; replaced with a single call to ToPropertyDescriptor(...). Local state used for manual parsing was deleted and descriptor construction is delegated to the helper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'Extract shared ToPropertyDescriptor helper' clearly and accurately summarizes the main change—extracting a shared helper function.
Description check ✅ Passed The description is comprehensive and well-structured, covering the summary, test plan with specific checkboxes, and issue closure. All required template sections are addressed.
Linked Issues check ✅ Passed The PR fully addresses issue #236: extracts ToPropertyDescriptor helper, implements ES2026 §6.2.5.5 validation, fixes the mixed descriptor rejection bug, unifies callable validation, and replaces duplicated parsing logic in both APIs.
Out of Scope Changes check ✅ Passed All changes directly align with issue #236 objectives: helper extraction, duplicate code removal, and behavioral fixes for descriptor validation. No unrelated modifications are present.
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.

Co-locate the helper with the descriptor types it constructs instead of
a separate utility unit. The implementation uses clause imports
ObjectValue to avoid a circular interface dependency.

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@units/Goccia.PropertyDescriptor.Utils.pas`:
- Around line 42-56: The code inherits Writable into PropertyFlags even when
creating an accessor descriptor, causing pfWritable to remain set despite
TGocciaPropertyDescriptorAccessor treating writability via FSetter; update the
creation path (e.g., inside ToPropertyDescriptor / the branch that constructs
TGocciaPropertyDescriptorAccessor when AExistingDescriptor is an accessor) to
clear pfWritable from PropertyFlags before constructing or assigning to
TGocciaPropertyDescriptorAccessor so the flag set matches accessor semantics
(remove pfWritable from PropertyFlags passed to
TGocciaPropertyDescriptorAccessor).
🪄 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: 7fac9f91-fa12-49c9-bb11-667bb2c4b6d0

📥 Commits

Reviewing files that changed from the base of the PR and between 1126fb9 and caf90e2.

📒 Files selected for processing (7)
  • AGENTS.md
  • docs/value-system.md
  • tests/built-ins/Object/defineProperty.js
  • tests/built-ins/Reflect/defineProperty.js
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.PropertyDescriptor.Utils.pas

Comment thread units/Goccia.PropertyDescriptor.Utils.pas Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 66 improved · 🔴 171 regressed · 37 unchanged · avg -3.1%
Bytecode: 🟢 130 improved · 🔴 52 regressed · 92 unchanged · avg +2.6%

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

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 4041 4041
Tests Passed 4000 ✅ 4041 ✅
Tests Skipped 41 0
Tests Test Duration 285.5ms 276.6ms
Tests Lex 80.2ms 54.4ms
Tests Parse 98.0ms 101.4ms
Tests Compile 67.0ms
Tests Execute 301.6ms 307.8ms
Tests Engine Total 479.7ms 530.6ms
Benchmarks Total 274 274
Benchmarks Duration 7.64min 6.77min

Measured on ubuntu-latest x64.

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.ObjectPropertyDescriptor.pas`:
- Around line 150-155: The code calls DescObj.HasProperty(PROP_WRITABLE)
multiple times which may reenter observable descriptor objects; cache the
presence check once in a local boolean (e.g. HasWritable :=
DescObj.HasProperty(PROP_WRITABLE)) and use that cached value for all subsequent
logic that uses the presence of PROP_WRITABLE when setting Writable and any
other branches that currently re-call HasProperty; apply the same caching
pattern where DescObj.HasProperty(PROP_ENUMERABLE) or
DescObj.HasProperty(PROP_CONFIGURABLE) are called multiple times to avoid extra
observable traps.
- Around line 55-60: To harden ToPropertyDescriptor, move the ES spec step-1
object validation into the helper: inside ToPropertyDescriptor check that
ADescriptorObject is a TGocciaObjectValue (or otherwise an object) before
casting, and if not raise the appropriate TypeError (e.g. EGocciaTypeError or
the project’s TGoccia type error) instead of performing the hard cast to
TGocciaObjectValue; update the code paths that previously assumed callers
validated (callers can keep their guards) and replace the direct cast with a
safe check + cast so ToPropertyDescriptor (TGocciaValue ->
TGocciaPropertyDescriptor) never causes an invalid-cast/AV when reused.
🪄 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: af9c9771-9d40-499c-bf09-f74457de3aef

📥 Commits

Reviewing files that changed from the base of the PR and between caf90e2 and 4ae9890.

📒 Files selected for processing (4)
  • docs/value-system.md
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
✅ Files skipped from review due to trivial changes (1)
  • docs/value-system.md

Comment thread units/Goccia.Values.ObjectPropertyDescriptor.pas
Comment thread units/Goccia.Values.ObjectPropertyDescriptor.pas
- Add step-1 object type guard so the helper throws TypeError on
  non-object input instead of relying on callers to validate
- Cache HasProperty(PROP_WRITABLE) in a local HasWritable boolean to
  avoid repeated observable property lookups (was called 3 times)
- Strip pfWritable from PropertyFlags before constructing accessor
  descriptors, since TGocciaPropertyDescriptorAccessor determines
  writability via FSetter, not the flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney merged commit a85b773 into main Apr 9, 2026
9 checks passed
@frostney frostney deleted the refactor/shared-to-property-descriptor branch April 9, 2026 09:51
@frostney frostney added the internal Refactoring, CI, tooling, cleanup label Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Refactoring, CI, tooling, cleanup

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: Extract shared ToPropertyDescriptor helper for Object.defineProperty and Reflect.defineProperty

1 participant