Skip to content

Fix Temporal BigInt duration overflow#431

Merged
frostney merged 8 commits into
mainfrom
t3code/bigint-spec-shortcuts
Apr 28, 2026
Merged

Fix Temporal BigInt duration overflow#431
frostney merged 8 commits into
mainfrom
t3code/bigint-spec-shortcuts

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 27, 2026

Summary

Fixes #420

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 27, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
gocciascript-homepage Ignored Ignored Preview Apr 28, 2026 9:02am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 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: a1ae0614-3474-4e0a-9e37-cbe092413389

📥 Commits

Reviewing files that changed from the base of the PR and between a2ac9d7 and cd2654f.

📒 Files selected for processing (4)
  • source/units/Goccia.Values.TemporalInstant.pas
  • tests/built-ins/Temporal/Instant/prototype/add.js
  • tests/built-ins/Temporal/Instant/prototype/round.js
  • tests/built-ins/Temporal/Instant/prototype/subtract.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/built-ins/Temporal/Instant/prototype/add.js
  • tests/built-ins/Temporal/Instant/prototype/subtract.js
  • source/units/Goccia.Values.TemporalInstant.pas

📝 Walkthrough

Walkthrough

Temporal internals switched to exact BigInteger math: added a DurationMath unit, migrated Duration components to TBigInteger, changed Instant/ZonedDateTime diffs/rounding to epoch-nanoseconds BigInteger arithmetic, added epoch-ns validation and new range error messages, and updated tests for big-range/formatting behavior.

Changes

Cohort / File(s) Summary
Duration Math Infrastructure
source/units/Goccia.Temporal.DurationMath.pas
New unit providing epoch-nanoseconds composition/diff, time-duration-from-components, BigInteger rounding, validators (IsValidEpochNanoseconds/IsValidTimeDuration), and balancing time-duration into fields.
Duration Value Refactor
source/units/Goccia.Values.TemporalDuration.pas
Migrated Duration internals to TBigInteger components (F*Big), added CreateFromBigIntegers, *Big properties, BigInteger arithmetic for add/sub/neg/round/total/compute, new ETemporalDurationInt64Overflow and BigInt-aware parsing/validation.
Instant & ZonedDateTime diffs/rounding
source/units/Goccia.Values.TemporalInstant.pas, source/units/Goccia.Values.TemporalZonedDateTime.pas
Replaced Int64 sub-day math with exact epoch-ns TBigInteger diffs, use RoundTimeDurationToIncrement and BalanceTimeDurationToFields, adjusted rounding divisor handling, and catch ETemporalDurationInt64Overflow to throw RangeError with suggestion.
Builtins, constructors & messages
source/units/Goccia.Builtins.Temporal.pas, source/units/Goccia.Error.Messages.pas, source/units/Goccia.Error.Suggestions.pas
Imported Goccia.Temporal.DurationMath; added IsValidEpochNanoseconds checks for epoch-ns constructors/fromEpochNanoseconds; new resource strings SErrorTemporalInstantOutOfRange and SSuggestTemporalInstantRange and thrown RangeErrors on invalid epoch-ns.
PlainDate/Time/DateTime/YearMonth handlers
source/units/Goccia.Values.TemporalPlainDate.pas, source/units/Goccia.Values.TemporalPlainTime.pas, source/units/Goccia.Values.TemporalPlainDateTime.pas, source/units/Goccia.Values.TemporalPlainYearMonth.pas
Wrapped add/sub flows in try/except to convert ETemporalDurationInt64Overflow into ThrowRangeError(..., SSuggestTemporalDurationRange) when duration balancing/creation overflows.
Instant add/sub/round normalization
source/units/Goccia.Values.TemporalInstant.pas (additional changes)
Instant add/sub use BigInteger totals for epoch-ms and sub-ms nanoseconds, normalize sub-ms nanoseconds, and translate ETemporalDurationInt64Overflow into RangeError with suggestion.
Tests: Temporal boundary, rounding & formatting
tests/built-ins/Temporal/...
Updated/added tests for epoch-ns boundary preservation and out-of-range errors, balanced toString formatting, largestUnit microseconds/nanoseconds handling, rounding/total/compare/from/with edge cases, and Instant add/sub normalization.

Sequence Diagram(s)

sequenceDiagram
    participant Client as rgba(52,152,219,0.5)
    participant Instant as rgba(46,204,113,0.5)
    participant DurationMath as rgba(155,89,182,0.5)
    participant DurationFactory as rgba(241,196,15,0.5)

    Client->>Instant: until(other, options)
    Instant->>DurationMath: EpochNanosecondsFromParts(epochMs, subMs)
    DurationMath-->>Instant: epochNs (TBigInteger)
    Instant->>DurationMath: TimeDurationFromEpochNanosecondsDifference(epochNs1, epochNs2)
    DurationMath-->>Instant: diffNs (TBigInteger)
    Instant->>DurationMath: RoundTimeDurationToIncrement(diffNs, increment, mode)
    DurationMath-->>Instant: roundedDiffNs
    Instant->>DurationMath: BalanceTimeDurationToFields(roundedDiffNs, largestUnit)
    DurationMath-->>Instant: h,m,s,ms,us,ns,days (TBigInteger fields)
    Instant->>DurationFactory: CreateFromBigIntegers(..., h,m,s,ms,us,ns)
    DurationFactory-->>Instant: TGocciaTemporalDurationValue
    Instant-->>Client: Duration result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • #417 — Prior Instant diff implementation that exhibited Int64 overflow; this PR implements the BigInteger-based fixes addressing that issue.
  • #346 — Overlapping changes to epoch-nanoseconds handling and constructor validation/error messages for Temporal.Instant.
  • #424 — Related until/since diff and rounding changes that interact with the newly added DurationMath helpers.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title "Fix Temporal BigInt duration overflow" clearly and directly describes the main fix addressing Int64 overflow in Temporal duration computations.
Description check ✅ Passed The PR description covers the key changes (BigInt helpers, routing diffs through nanosecond totals, string formatting, validation), references the linked issue (#420), but lacks testing and documentation verification checkboxes from the template.
Linked Issues check ✅ Passed The PR successfully addresses issue #420 by implementing BigInt-backed duration math helpers, routing Instant/ZonedDateTime diffs through BigInt nanosecond totals to prevent Int64 overflow, validating epoch-nanoseconds ranges, and adding comprehensive test coverage for microsecond/nanosecond edge cases.
Out of Scope Changes check ✅ Passed All changes align with fixing the Int64 overflow issue: new DurationMath unit, BigInt-backed Duration storage, overflow exception handling in related temporal types, and test additions for edge cases. No unrelated refactoring or scope creep 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.

@coderabbitai coderabbitai Bot added bug Something isn't working spec compliance Mismatch against official JavaScript/TypeScript specification internal Refactoring, CI, tooling, cleanup labels Apr 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 8052 8052
Tests Passed 8011 ✅ 8052 ✅
Tests Skipped 41 0
Tests Workers 4 4
Tests Test Duration 1.91s 1.71s
Tests Lex (cumulative) 210.9ms 127.9ms
Tests Parse (cumulative) 309.6ms 296.8ms
Tests Compile (cumulative) 183.6ms
Tests Execute (cumulative) 1.74s 1.53s
Tests Engine Total (cumulative) 2.26s 2.14s
Tests Lex (avg/worker) 52.7ms 32.0ms
Tests Parse (avg/worker) 77.4ms 74.2ms
Tests Compile (avg/worker) 45.9ms
Tests Execute (avg/worker) 434.4ms 382.3ms
Tests Engine Total (avg/worker) 564.6ms 534.4ms
Benchmarks Total 386 386
Benchmarks Workers 4 4
Benchmarks Duration 3.00min 2.80min

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

Benchmark Results

386 benchmarks

Interpreted: 🟢 157 improved · 🔴 8 regressed · 221 unchanged · avg +3.4%
Bytecode: 🟢 19 improved · 🔴 324 regressed · 43 unchanged · avg -7.1%

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

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 `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 175-240: BigIntToLegacyInt64 currently returns 0 for out-of-range
TBigInteger which silently corrupts the legacy Int64 mirrors
(FYears..FNanoseconds) while the bigint fields (FYearsBig..FNanosecondsBig) keep
the real values; change the behavior to fail fast: modify BigIntToLegacyInt64 to
raise an exception (e.g. ERangeError) when AValue is outside Int64 bounds, and
update callers (SetDurationFields, TGocciaTemporalDurationValue.Create/
CreateFromBigIntegers) to allow that exception to propagate so code doesn’t
silently zero legacy mirrors—this ensures duration arithmetic uses consistent
bigint data or stops on overflow instead of mismatched state.

In `@tests/built-ins/Temporal/ZonedDateTime/prototype/until.js`:
- Around line 41-47: The test "until() with largestUnit microseconds does not
overflow" currently asserts dur.microseconds against a numeric literal that
exceeds Number.MAX_SAFE_INTEGER; change the assertion to compare a safe
representation such as the string form or a decomposition: call dur.toString()
and assert it equals the expected string, or split dur into safe integer
components (e.g., seconds and remaining microseconds) and assert those instead;
update the assertion referencing z1, z2, and dur (result of z1.until(z2, {
largestUnit: "microseconds" })) so you no longer compare dur.microseconds to an
unsafe JS number.
🪄 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: 2a98467b-9767-4e04-a902-695e36f81e02

📥 Commits

Reviewing files that changed from the base of the PR and between b22faae and dce379d.

📒 Files selected for processing (13)
  • source/units/Goccia.Builtins.Temporal.pas
  • source/units/Goccia.Error.Messages.pas
  • source/units/Goccia.Error.Suggestions.pas
  • source/units/Goccia.Temporal.DurationMath.pas
  • source/units/Goccia.Values.TemporalDuration.pas
  • source/units/Goccia.Values.TemporalInstant.pas
  • source/units/Goccia.Values.TemporalZonedDateTime.pas
  • tests/built-ins/Temporal/Duration/prototype/toString.js
  • tests/built-ins/Temporal/Instant/fromEpochNanoseconds.js
  • tests/built-ins/Temporal/Instant/prototype/since.js
  • tests/built-ins/Temporal/Instant/prototype/until.js
  • tests/built-ins/Temporal/ZonedDateTime/prototype/since.js
  • tests/built-ins/Temporal/ZonedDateTime/prototype/until.js

Comment thread source/units/Goccia.Values.TemporalDuration.pas Outdated
Comment thread tests/built-ins/Temporal/ZonedDateTime/prototype/until.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
source/units/Goccia.Values.TemporalDuration.pas (2)

1188-1193: ⚠️ Potential issue | 🟠 Major

Duration.prototype.total() still demotes bigint-backed durations through checked Int64 accessors.

SubDayNanoseconds, ComputeTotalInUnits, and the ResolvedDays branch all read D.* via the checked Int64 properties, so total() becomes unusable on the same large nanosecond/microsecond durations this PR now produces. The accumulation should stay in TBigInteger/DurationMath until the final Number conversion at the API boundary. Based on learnings: the earlier microsecond/nanosecond overflow was intentionally deferred until TGocciaTemporalDurationValue became BigInt-backed.

May an implementation of `Temporal.Duration.prototype.total()` reject a valid duration only because one internal time component exceeds 64-bit integer range, or is the computation expected to use arbitrary-precision duration math until the final Number result?

Also applies to: 1217-1217, 1339-1355

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

In `@source/units/Goccia.Values.TemporalDuration.pas` around lines 1188 - 1193,
Duration.prototype.total() currently reads D.* via checked Int64 accessors
(e.g., SubDayNanoseconds, ComputeTotalInUnits, ResolvedDays and code paths using
TGocciaTemporalDurationValue), which demotes BigInt-backed durations and can
overflow; change these code paths to use the BigInteger/DurationMath
representations (keep accumulation in TBigInteger or DurationMath) and only
convert to Number at the API boundary (the final return), avoiding any use of
the checked Int64 accessors for intermediate reads so large
nanosecond/microsecond durations remain precise.

737-746: ⚠️ Potential issue | 🟠 Major

Duration.prototype.round() is still Int64-bound.

Lines 737-746 and 776-997 still route the new BigInt-backed fields through D.Years/D.Nanoseconds and Int64 accumulators like TotalNs. A valid duration produced by Instant.until(..., { largestUnit: "microsecond" | "nanosecond" }) can now exceed High(Int64), so round() raises before it ever rounds. This path needs to stay on F*Big/TBigInteger, and the result should be built with CreateFromBigIntegers whenever the rounded component can still be out of Int64 range. Based on learnings: the earlier microsecond/nanosecond overflow was intentionally deferred until TGocciaTemporalDurationValue became BigInt-backed.

Does the TC39 Temporal specification require `Temporal.Duration.prototype.round()` to operate on valid large durations returned from `Temporal.Instant.prototype.until(..., { largestUnit: "nanosecond" })`, even when the internal nanosecond count exceeds 64-bit integer range?

Also applies to: 755-757, 776-787, 990-997

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

In `@source/units/Goccia.Values.TemporalDuration.pas` around lines 737 - 746,
Duration.prototype.round currently uses Int64-bound accessors (D.Years,
D.Nanoseconds) and accumulators (TotalNs) which overflow for BigInt-backed
durations; change the rounding path to operate on the BigInt fields (F*Big /
TBigInteger) throughout instead of D.*/Int64 accumulators, and when constructing
the result use TGocciaTemporalDurationValue.CreateFromBigIntegers so the
returned Duration can contain components outside Int64 range; update all
affected branches referenced in the diff (the D.Years..D.Microseconds checks,
the TotalNs calculations, and the final construction paths around lines noted)
to use the F*Big/TBigInteger variants and CreateFromBigIntegers where the
component or total may exceed High(Int64).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 428-449: The fractional subsecond trimming in ToISOString
currently conditions how many digits to keep based on FNanosecondsBig/FM
icrosecondsBig/FM illisecondsBig; instead, after computing SubSecondsPart and
building Fraction via Format('%.9d', [SubSecondsPart.ToInt64]) (around
SecondsDuration/AbsSecondsDuration/SecondsPart/SubSecondsPart/Fraction), always
strip trailing '0' characters from Fraction (loop while Length(Fraction)>0 and
last char='0' then delete) so the recombined nanoseconds are reduced to the
minimal representation (e.g., "200000000" -> "2"); remove the conditional
branches that truncate to 3 or 6 digits and ensure if Fraction becomes empty you
handle it the same way as the current "no fractional part" case.

---

Duplicate comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 1188-1193: Duration.prototype.total() currently reads D.* via
checked Int64 accessors (e.g., SubDayNanoseconds, ComputeTotalInUnits,
ResolvedDays and code paths using TGocciaTemporalDurationValue), which demotes
BigInt-backed durations and can overflow; change these code paths to use the
BigInteger/DurationMath representations (keep accumulation in TBigInteger or
DurationMath) and only convert to Number at the API boundary (the final return),
avoiding any use of the checked Int64 accessors for intermediate reads so large
nanosecond/microsecond durations remain precise.
- Around line 737-746: Duration.prototype.round currently uses Int64-bound
accessors (D.Years, D.Nanoseconds) and accumulators (TotalNs) which overflow for
BigInt-backed durations; change the rounding path to operate on the BigInt
fields (F*Big / TBigInteger) throughout instead of D.*/Int64 accumulators, and
when constructing the result use
TGocciaTemporalDurationValue.CreateFromBigIntegers so the returned Duration can
contain components outside Int64 range; update all affected branches referenced
in the diff (the D.Years..D.Microseconds checks, the TotalNs calculations, and
the final construction paths around lines noted) to use the F*Big/TBigInteger
variants and CreateFromBigIntegers where the component or total may exceed
High(Int64).
🪄 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: a8b215ee-713f-4591-b36a-4d60af8e94f6

📥 Commits

Reviewing files that changed from the base of the PR and between dce379d and 9614634.

📒 Files selected for processing (5)
  • source/units/Goccia.Values.TemporalDuration.pas
  • tests/built-ins/Temporal/Instant/prototype/since.js
  • tests/built-ins/Temporal/Instant/prototype/until.js
  • tests/built-ins/Temporal/ZonedDateTime/prototype/since.js
  • tests/built-ins/Temporal/ZonedDateTime/prototype/until.js
✅ Files skipped from review due to trivial changes (1)
  • tests/built-ins/Temporal/Instant/prototype/until.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/built-ins/Temporal/ZonedDateTime/prototype/until.js

Comment thread source/units/Goccia.Values.TemporalDuration.pas Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

853-863: ⚠️ Potential issue | 🟠 Major

Use the shared negative bucket formula here as well.

Both calendar-unit branches still align negative values with WholeUnits - 1. That is the old off-by-one bucket selection and it can over-round negative year/month results, especially with roundingIncrement = 1.

Suggested fix
-        if Sign < 0 then
-          ScaledValue := WholeUnits - 1
-        else
-          ScaledValue := WholeUnits;
-        if (ScaledValue >= 0) or (ScaledValue mod Increment = 0) then
-          ScaledValue := (ScaledValue div Increment) * Increment
-        else
-          ScaledValue := ((ScaledValue div Increment) - 1) * Increment;
+        if Sign < 0 then
+          ScaledValue := -(((-WholeUnits) div Increment) * Increment)
+        else if (WholeUnits >= 0) or (WholeUnits mod Increment = 0) then
+          ScaledValue := (WholeUnits div Increment) * Increment
+        else
+          ScaledValue := ((WholeUnits div Increment) - 1) * Increment;

Based on learnings: In source/units/Goccia.Temporal.Options.pas (RoundDiffDuration, calendar-unit rounding path for tuYear/tuMonth), the correct negative alignment is ScaledValue := -(((-WholeUnits) div AIncrement) * AIncrement); WholeUnits - 1 was the prior bug.

Also applies to: 903-911

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

In `@source/units/Goccia.Values.TemporalDuration.pas` around lines 853 - 863, The
negative-alignment branch that currently sets ScaledValue := WholeUnits - 1
should be replaced with the shared negative-bucket formula used in
RoundDiffDuration: compute ScaledValue for negative values as -(((-WholeUnits)
div Increment) * Increment) instead of WholeUnits - 1; update the branch that
checks Sign < 0 (and the equivalent mirror at the other occurrence around lines
903-911) so the negative alignment and subsequent bucketing use WholeUnits via
that formula while keeping the existing positive-path logic and the later
mod/div adjustments unchanged.
♻️ Duplicate comments (1)
source/units/Goccia.Values.TemporalDuration.pas (1)

34-43: ⚠️ Potential issue | 🟠 Major

Keep the Pascal accessors BigInt-safe too.

The class can now hold spec-valid duration components that exceed High(Int64) in a single field, but these public properties still expose them only through checked Int64 getters. That means TGocciaTemporalDurationValue can represent a value that its Pascal API immediately rejects, which is a fragile surface for any remaining internal callers.

Also applies to: 56-65, 293-341

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

In `@source/units/Goccia.Values.TemporalDuration.pas` around lines 34 - 43, The
Int64 getters (GetYearsInt64, GetMonthsInt64, GetWeeksInt64, GetDaysInt64,
GetHoursInt64, GetMinutesInt64, GetSecondsInt64, GetMillisecondsInt64,
GetMicrosecondsInt64, GetNanosecondsInt64) expose values with checked Int64
semantics while the TGocciaTemporalDurationValue can hold components larger than
High(Int64); update these accessors to be BigInt-safe by returning the
unlimited-precision integer type used in the module (or by adding corresponding
BigInt getters/properties) and ensure any existing callers use the new
BigInt-returning methods (or properly handle overflows) so the public Pascal API
can represent spec-valid large components without truncation or exceptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 666-675: GetFieldOr is truncating via Trunc(...) and
TBigInteger.FromDouble which forces values through Int64/double and overflows
large object-supplied integers; replace that conversion so we build a
TBigInteger directly from the value text or native bigint representation instead
of truncating to Int64. Specifically, in GetFieldOr use the object's
bigint-aware accessor (or parse the numeric literal string) to produce a
TBigInteger (e.g. construct from Val.ToString/ToStringLiteral or a ToBigInteger
method if available) instead of
TBigInteger.FromDouble(Trunc(Val.ToNumberLiteral.Value)) so large integers are
preserved.

---

Outside diff comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 853-863: The negative-alignment branch that currently sets
ScaledValue := WholeUnits - 1 should be replaced with the shared negative-bucket
formula used in RoundDiffDuration: compute ScaledValue for negative values as
-(((-WholeUnits) div Increment) * Increment) instead of WholeUnits - 1; update
the branch that checks Sign < 0 (and the equivalent mirror at the other
occurrence around lines 903-911) so the negative alignment and subsequent
bucketing use WholeUnits via that formula while keeping the existing
positive-path logic and the later mod/div adjustments unchanged.

---

Duplicate comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 34-43: The Int64 getters (GetYearsInt64, GetMonthsInt64,
GetWeeksInt64, GetDaysInt64, GetHoursInt64, GetMinutesInt64, GetSecondsInt64,
GetMillisecondsInt64, GetMicrosecondsInt64, GetNanosecondsInt64) expose values
with checked Int64 semantics while the TGocciaTemporalDurationValue can hold
components larger than High(Int64); update these accessors to be BigInt-safe by
returning the unlimited-precision integer type used in the module (or by adding
corresponding BigInt getters/properties) and ensure any existing callers use the
new BigInt-returning methods (or properly handle overflows) so the public Pascal
API can represent spec-valid large components without truncation or exceptions.
🪄 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: 9acc65dd-90ec-483d-93ca-c4a0c826ee22

📥 Commits

Reviewing files that changed from the base of the PR and between 9614634 and b9363b2.

📒 Files selected for processing (4)
  • source/units/Goccia.Values.TemporalDuration.pas
  • tests/built-ins/Temporal/Duration/prototype/round.js
  • tests/built-ins/Temporal/Duration/prototype/toString.js
  • tests/built-ins/Temporal/Duration/prototype/total.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/built-ins/Temporal/Duration/prototype/toString.js

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 201-219: Replace the plain Pascal Exception raises in
NumberToBigInteger with the proper Temporal/JS error classes: when IsNaN(AValue)
or IsInfinite(AValue) raise the JS RangeError (e.g. ERangeError or the project’s
Temporal RangeError wrapper) with a clear message, and when Frac(AValue) <> 0
raise the JS TypeError/RangeError as appropriate for your Temporal
implementation (use the project’s EJSTypeError or ERangeError class) instead of
Exception.Create; keep the rest of NumberToBigInteger and ValueToBigInteger
logic unchanged and reference the function name NumberToBigInteger when making
the changes.
🪄 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: 93798ea6-8f2a-4036-8821-e35923523ef8

📥 Commits

Reviewing files that changed from the base of the PR and between b9363b2 and 68dd3f3.

📒 Files selected for processing (5)
  • source/units/Goccia.Builtins.Temporal.pas
  • source/units/Goccia.Values.TemporalDuration.pas
  • tests/built-ins/Temporal/Duration/compare.js
  • tests/built-ins/Temporal/Duration/from.js
  • tests/built-ins/Temporal/Duration/prototype/with.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/units/Goccia.Builtins.Temporal.pas

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

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

1120-1121: ⚠️ Potential issue | 🟠 Major

Reject ZonedDateTime here until the DST-aware relativeTo path exists.

Collapsing relativeTo to a plain date loses the zone/instant context needed for variable-length days, so Duration.prototype.total() will now silently treat a DST-crossing ZonedDateTime as a fixed 24-hour day. It’s safer to keep throwing for this input until the timezone-aware path is implemented. Based on learnings: support for TGocciaTemporalZonedDateTimeValue in ParseRelativeTo for Duration.prototype.total() was intentionally deferred because DST-aware day-length handling is still missing.

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

In `@source/units/Goccia.Values.TemporalDuration.pas` around lines 1120 - 1121, In
the branch that currently accepts TGocciaTemporalZonedDateTimeValue (the else if
AValue is TGocciaTemporalZonedDateTimeValue then Result :=
ZonedDateTimeToDateRecord(...)) reject this type instead: update the
ParseRelativeTo logic (or the enclosing method containing that branch) to
raise/return an error for TGocciaTemporalZonedDateTimeValue rather than
converting it to a plain date, and add a comment referencing that DST-aware
handling is required (keep the ZonedDateTimeToDateRecord call removed/disabled
until TGocciaTemporalZonedDateTimeValue is supported by the timezone-aware
relativeTo path).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 188-193: BigIntToCheckedInt64 currently raises a raw ERangeError
when the BigInt is out of Int64 range; create a specific exception (e.g.,
ETemporalOverflow or ETemporalRangeError inheriting from Exception) and have
BigIntToCheckedInt64 raise that instead (with the same formatted message) so the
JS-facing layers can catch/translate it instead of letting a raw Pascal
ERangeError escape; update the function BigIntToCheckedInt64 to raise the new
exception type and ensure any callers of the Years..Nanoseconds accessors will
be able to catch and convert this new exception for proper JS error handling.

---

Outside diff comments:
In `@source/units/Goccia.Values.TemporalDuration.pas`:
- Around line 1120-1121: In the branch that currently accepts
TGocciaTemporalZonedDateTimeValue (the else if AValue is
TGocciaTemporalZonedDateTimeValue then Result := ZonedDateTimeToDateRecord(...))
reject this type instead: update the ParseRelativeTo logic (or the enclosing
method containing that branch) to raise/return an error for
TGocciaTemporalZonedDateTimeValue rather than converting it to a plain date, and
add a comment referencing that DST-aware handling is required (keep the
ZonedDateTimeToDateRecord call removed/disabled until
TGocciaTemporalZonedDateTimeValue is supported by the timezone-aware relativeTo
path).
🪄 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: b953a421-fc1a-49dd-9960-329d86b10989

📥 Commits

Reviewing files that changed from the base of the PR and between 68dd3f3 and 168a76e.

📒 Files selected for processing (2)
  • source/units/Goccia.Values.TemporalDuration.pas
  • tests/built-ins/Temporal/Duration/prototype/with.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/built-ins/Temporal/Duration/prototype/with.js

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

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

259-266: ⚠️ Potential issue | 🔴 Critical

Use Int64 to accumulate sub-millisecond nanoseconds before normalization.

When Dur.Microseconds exceeds ~2.1 million, the expression Integer(Dur.Microseconds * 1000 + Dur.Nanoseconds) overflows the 32-bit integer range and wraps silently. The exception handler only catches ETemporalDurationInt64Overflow from property access, not the Integer overflow itself, so the wrapped value corrupts the result.

Accumulate in Int64 and normalize in-place:

SubNsTotal := Int64(Inst.FSubMillisecondNanoseconds) + (Dur.Microseconds * 1000 + Dur.Nanoseconds);
if SubNsTotal >= 0 then
begin
  Inc(NewMs, SubNsTotal div 1000000);
  NewSubMs := Integer(SubNsTotal mod 1000000);
end
else
begin
  CarryMs := (SubNsTotal - 999999) div 1000000;
  Inc(NewMs, CarryMs);
  NewSubMs := Integer(SubNsTotal - CarryMs * 1000000);
end;

Apply the same fix to InstantSubtract (line 307–314).

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

In `@source/units/Goccia.Values.TemporalInstant.pas` around lines 259 - 266, The
code currently computes NewSubMs using Integer(Dur.Microseconds * 1000 +
Dur.Nanoseconds) which can overflow 32-bit; change the accumulation to use an
Int64 temporary (e.g., SubNsTotal := Int64(Inst.FSubMillisecondNanoseconds) +
Int64(Dur.Microseconds) * 1000 + Int64(Dur.Nanoseconds)), then normalize by
carrying whole milliseconds into NewMs and leaving the remainder in NewSubMs
(handling both positive and negative totals correctly), replacing the direct
Integer cast; make the identical change in the corresponding InstantSubtract
logic so both InstantAdd/InstantSubtract use Int64 accumulation and proper
normalization of sub-millisecond nanoseconds.
🤖 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 `@source/units/Goccia.Values.TemporalInstant.pas`:
- Around line 259-266: The code currently computes NewSubMs using
Integer(Dur.Microseconds * 1000 + Dur.Nanoseconds) which can overflow 32-bit;
change the accumulation to use an Int64 temporary (e.g., SubNsTotal :=
Int64(Inst.FSubMillisecondNanoseconds) + Int64(Dur.Microseconds) * 1000 +
Int64(Dur.Nanoseconds)), then normalize by carrying whole milliseconds into
NewMs and leaving the remainder in NewSubMs (handling both positive and negative
totals correctly), replacing the direct Integer cast; make the identical change
in the corresponding InstantSubtract logic so both InstantAdd/InstantSubtract
use Int64 accumulation and proper normalization of sub-millisecond nanoseconds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1674536b-1b30-41b0-9507-0de85de274f6

📥 Commits

Reviewing files that changed from the base of the PR and between 68dd3f3 and 7b1f8b4.

📒 Files selected for processing (10)
  • source/units/Goccia.Values.TemporalDuration.pas
  • source/units/Goccia.Values.TemporalInstant.pas
  • source/units/Goccia.Values.TemporalPlainDate.pas
  • source/units/Goccia.Values.TemporalPlainDateTime.pas
  • source/units/Goccia.Values.TemporalPlainTime.pas
  • source/units/Goccia.Values.TemporalPlainYearMonth.pas
  • source/units/Goccia.Values.TemporalZonedDateTime.pas
  • tests/built-ins/Temporal/Duration/prototype/round.js
  • tests/built-ins/Temporal/Duration/prototype/total.js
  • tests/built-ins/Temporal/Duration/prototype/with.js
✅ Files skipped from review due to trivial changes (1)
  • tests/built-ins/Temporal/Duration/prototype/with.js

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (3)
source/units/Goccia.Values.TemporalInstant.pas (3)

437-489: ⚠️ Potential issue | 🟠 Major

Build the rounding divisor in BigInteger space.

UnitToNanoseconds(SmallestUnit) * Increment still multiplies in Int64 before conversion, so a legal large roundingIncrement can overflow here, especially when rounding by hours. Convert each operand to TBigInteger first, then multiply.

Proposed fix
-  BigDivisor := TBigInteger.FromInt64(UnitToNanoseconds(SmallestUnit) * Increment);
+  BigDivisor := TBigInteger.FromInt64(UnitToNanoseconds(SmallestUnit))
+    .Multiply(TBigInteger.FromInt64(Increment));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Values.TemporalInstant.pas` around lines 437 - 489, In
InstantRound, BigDivisor is currently built by multiplying
UnitToNanoseconds(SmallestUnit) * Increment in Int64 then converting to
TBigInteger, which can overflow; change construction so both operands are
converted to TBigInteger first (e.g. BigDivisor :=
TBigInteger.FromInt64(UnitToNanoseconds(SmallestUnit)).Multiply(TBigInteger.FromInt32(Increment)))
so the multiplication happens in big-int space before passing to
RoundBigIntWithMode; update references in InstantRound (BigDivisor, BigTotal,
BigMillion, RoundBigIntWithMode usage) accordingly.

295-343: ⚠️ Potential issue | 🟠 Major

Same overflow gap in subtract().

This path has the same problem as add(): the new exception handler does not protect the Int64 arithmetic used to scale the duration components, so large valid inputs can still produce the wrong instant. Please switch the whole subtraction path to BigInteger math before normalizing the sub-millisecond remainder.

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

In `@source/units/Goccia.Values.TemporalInstant.pas` around lines 295 - 343, The
subtraction path in TGocciaTemporalInstantValue.InstantSubtract uses Int64 math
for scaling duration components (variables NewMs and SubNsTotal) and can
overflow; change the computation to use BigInteger (or an arbitrary-precision
integer type) for the whole subtraction sequence (subtract
days/hours/minutes/seconds/milliseconds and micro/nanosecond parts) and only
convert the final results back to Int64/Integer when calling
NormalizeInstantSubMillisecondNanoseconds and creating the result; keep the
existing ETemporalDurationInt64Overflow handler around the conversion so
large-but-valid durations are computed safely before normalizing sub-millisecond
remainder and returning TGocciaTemporalInstantValue.Create(NewMs, NewSubMs).

243-292: ⚠️ Potential issue | 🟠 Major

Move the add-path delta math off Int64.

The new try/except only catches ETemporalDurationInt64Overflow; the Dur.Days..Dur.Milliseconds scaling and sub-millisecond accumulation still use Int64 math and can wrap before normalization for large but valid durations. Please accumulate the full delta in TBigInteger, then normalize back to milliseconds/nanoseconds.

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

In `@source/units/Goccia.Values.TemporalInstant.pas` around lines 243 - 292,
InstantAdd currently multiplies Dur fields into Int64 (NewMs/SubNsTotal) and can
overflow before the try/except; change the delta accumulation to use
TBigInteger: compute totalMilliseconds = TBigInteger(Dur.Days)*86400000 +
TBigInteger(Dur.Hours)*3600000 + ... + TBigInteger(Dur.Milliseconds) and
totalNanoseconds = TBigInteger(Inst.FSubMillisecondNanoseconds) +
TBigInteger(Dur.Microseconds)*NANOSECONDS_PER_MICROSECOND +
TBigInteger(Dur.Nanoseconds), then convert the combined big-integer values into
Int64 NewMs and Integer NewSubMs only after calling
NormalizeInstantSubMillisecondNanoseconds (or an adapted normalization that
accepts big integers), and keep the existing except on
ETemporalDurationInt64Overflow to ThrowRangeError; update references to
NewMs/SubNsTotal to use the big-integer temporaries and ensure any conversion
detects overflow and triggers the same range error.
🤖 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 `@source/units/Goccia.Values.TemporalInstant.pas`:
- Around line 437-489: In InstantRound, BigDivisor is currently built by
multiplying UnitToNanoseconds(SmallestUnit) * Increment in Int64 then converting
to TBigInteger, which can overflow; change construction so both operands are
converted to TBigInteger first (e.g. BigDivisor :=
TBigInteger.FromInt64(UnitToNanoseconds(SmallestUnit)).Multiply(TBigInteger.FromInt32(Increment)))
so the multiplication happens in big-int space before passing to
RoundBigIntWithMode; update references in InstantRound (BigDivisor, BigTotal,
BigMillion, RoundBigIntWithMode usage) accordingly.
- Around line 295-343: The subtraction path in
TGocciaTemporalInstantValue.InstantSubtract uses Int64 math for scaling duration
components (variables NewMs and SubNsTotal) and can overflow; change the
computation to use BigInteger (or an arbitrary-precision integer type) for the
whole subtraction sequence (subtract days/hours/minutes/seconds/milliseconds and
micro/nanosecond parts) and only convert the final results back to Int64/Integer
when calling NormalizeInstantSubMillisecondNanoseconds and creating the result;
keep the existing ETemporalDurationInt64Overflow handler around the conversion
so large-but-valid durations are computed safely before normalizing
sub-millisecond remainder and returning
TGocciaTemporalInstantValue.Create(NewMs, NewSubMs).
- Around line 243-292: InstantAdd currently multiplies Dur fields into Int64
(NewMs/SubNsTotal) and can overflow before the try/except; change the delta
accumulation to use TBigInteger: compute totalMilliseconds =
TBigInteger(Dur.Days)*86400000 + TBigInteger(Dur.Hours)*3600000 + ... +
TBigInteger(Dur.Milliseconds) and totalNanoseconds =
TBigInteger(Inst.FSubMillisecondNanoseconds) +
TBigInteger(Dur.Microseconds)*NANOSECONDS_PER_MICROSECOND +
TBigInteger(Dur.Nanoseconds), then convert the combined big-integer values into
Int64 NewMs and Integer NewSubMs only after calling
NormalizeInstantSubMillisecondNanoseconds (or an adapted normalization that
accepts big integers), and keep the existing except on
ETemporalDurationInt64Overflow to ThrowRangeError; update references to
NewMs/SubNsTotal to use the big-integer temporaries and ensure any conversion
detects overflow and triggers the same range error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eab049dc-aeb9-4b47-a18d-94b61624d671

📥 Commits

Reviewing files that changed from the base of the PR and between 7b1f8b4 and a2ac9d7.

📒 Files selected for processing (3)
  • source/units/Goccia.Values.TemporalInstant.pas
  • tests/built-ins/Temporal/Instant/prototype/add.js
  • tests/built-ins/Temporal/Instant/prototype/subtract.js
✅ Files skipped from review due to trivial changes (1)
  • tests/built-ins/Temporal/Instant/prototype/add.js

@frostney frostney merged commit 9ac5291 into main Apr 28, 2026
12 checks passed
@frostney frostney deleted the t3code/bigint-spec-shortcuts branch April 28, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

InstantDiffToUnits: Int64 overflow in tuMicrosecond/tuNanosecond branches (requires BigInt-backed Duration)

1 participant