Skip to content

Implement TC39 Template Literal Revision for tagged templates#281

Merged
frostney merged 4 commits into
mainfrom
feature/template-literal-revision
Apr 12, 2026
Merged

Implement TC39 Template Literal Revision for tagged templates#281
frostney merged 4 commits into
mainfrom
feature/template-literal-revision

Conversation

@frostney
Copy link
Copy Markdown
Owner

Summary

  • Implement the TC39 Template Literal Revision (ES2018): tagged template literals now tolerate malformed escape sequences (\u{, \xG1, incomplete \u escapes, code points > U+10FFFF) by setting the cooked segment to undefined while preserving the raw source text in strings.raw.
  • Untagged template literals continue to throw SyntaxError for the same invalid escapes, preserving existing behavior.
  • The implementation spans the full pipeline: lexer (template-aware escape scanners), parser (per-segment re-cooking from raw), AST (CookedValid flags), evaluator, compiler, bytecode serialization (.gbc), and VM.
  • Closes Template Literal Revision: support cooked=undefined for malformed escapes in tagged templates #269

Testing

  • Verified no regressions and confirmed the new feature or bugfix in end-to-end JavaScript/TypeScript tests
    • 13 new test cases in tagged-templates.js covering \xG1, \u{, \u{ZZZZ}, \u00, \xQQ, mixed valid/invalid segments, frozen objects, expression evaluation, call-site identity, code point > U+10FFFF, and valid escapes still working
    • All 48 string expression tests pass in both interpreted and bytecode modes
    • Full test suite (3602 tests) passes with no new regressions
    • .gbc serialization round-trip verified for templates with invalid escapes
  • Updated documentation
    • docs/language-restrictions.md updated to document TC39 Template Literal Revision support

🤖 Generated with Claude Code

Tagged template literals now tolerate malformed escape sequences (\u{,
\xG1, incomplete \u escapes, code points > U+10FFFF) by setting the
cooked segment to undefined while preserving the raw source text.
Untagged template literals continue to throw SyntaxError for the same
invalid escapes, preserving existing behavior.

Closes #269

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

coderabbitai Bot commented Apr 12, 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: 5841e685-a039-479c-bfe7-07e6ee22f363

📥 Commits

Reviewing files that changed from the base of the PR and between 7d064ff and 8f4bee5.

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

📝 Walkthrough

Walkthrough

Lexer, parser, AST, bytecode, compiler, evaluator, and VM updated to implement ES2018 Template Literal Revision: tagged templates now carry per-segment cooked-valid flags so malformed \u/\x escapes yield cooked = undefined while raw preserves source; untagged templates still throw SyntaxError.

Changes

Cohort / File(s) Summary
Docs & Tests
docs/language-restrictions.md, tests/language/expressions/string/tagged-templates.js, tests/language/expressions/string/template-string.js
Docs updated to note ES2018 template behavior; added extensive tagged-template tests for malformed escapes and a trailing newline fix.
Lexer
units/Goccia.Lexer.pas
Adds template-aware escape scanners and ProcessTemplateEscapeSequence; tracks per-segment validity and emits alternate token separator when invalid escapes occur (avoids immediate lexer exceptions for tagged templates).
Parser
units/Goccia.Parser.pas
New helpers to split/cook raw segments; ExtractTemplateParts reports HasInvalidEscapes; ParseTaggedTemplate / ParseTemplateLiteral handle invalid-escape paths and produce CookedValid arrays.
AST / Data Model
units/Goccia.AST.Expressions.pas
Introduces TGocciaTemplateCookedValid and extends TGocciaTaggedTemplateExpression with FCookedValid / CookedValid and updated constructor.
Bytecode (Chunk & Binary)
units/Goccia.Bytecode.Chunk.pas, units/Goccia.Bytecode.Binary.pas
Adds CookedValid to template-object constants; AddConstantTemplateObject and bytecode writer/reader serialize/deserialize per-segment cooked-valid booleans.
Compiler
units/Goccia.Compiler.Expressions.pas
CompileTaggedTemplate now passes AExpr.CookedValid when emitting the call-site template-object constant.
Evaluator / VM
units/Goccia.Evaluator.pas, units/Goccia.VM.pas
When building template objects, uses CookedValid to insert undefined for malformed cooked segments while preserving raw strings and existing caching/freezing behavior.

Sequence Diagram(s)

sequenceDiagram
    participant Lexer as Lexer
    participant Parser as Parser
    participant Compiler as Compiler
    participant Bytecode as Bytecode
    participant VM as VM
    participant Evaluator as Evaluator
    participant Tag as TagFunction

    Lexer->>Parser: Tokenize template (mark invalid escapes, keep raw)
    Parser->>Compiler: Build TGocciaTaggedTemplateExpression (CookedStrings, RawStrings, CookedValid)
    Compiler->>Bytecode: AddConstantTemplateObject(..., CookedValid)
    Bytecode->>VM: Load template constant (contains CookedValid)
    VM->>Evaluator: Build template object (use CookedValid → cooked or undefined)
    Evaluator->>Tag: Call tag with template object and interpolations
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #269 — Direct match: implements Template Literal Revision semantics (cooked = undefined for malformed escapes in tagged templates).
  • #279 — Related lexer/parser template segmentation changes; both touch ScanTemplate and parser boundary handling.

Possibly related PRs

  • #267 — Prior tagged-template support; this PR extends the same lexer/parser/AST/evaluator paths to add per-segment validity.
  • #275 — Overlapping edits to tagged-template AST/bytecode/compiler/VM surfaces; closely related at code-level.
  • #278 — Related lexer/parser fixes for template escape/boundary handling affecting similar code paths.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Implement TC39 Template Literal Revision for tagged templates' clearly and specifically summarizes the main change, which is adding ES2018 TC39 Template Literal Revision support for tagged templates.
Description check ✅ Passed The PR description comprehensively covers the summary of changes, implementation scope, testing approach with concrete test counts/coverage, and documentation updates, following the template structure with all key sections filled appropriately.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #269: lexer escape handling with validity flags, parser per-segment re-cooking, AST CookedValid metadata, evaluator undefined emission, bytecode serialization, VM support, and comprehensive test coverage for both tagged and untagged templates.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to implementing TC39 Template Literal Revision for tagged templates as specified in issue #269. No extraneous changes detected across lexer, parser, AST, evaluator, compiler, bytecode, or VM modifications.
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 new feature New feature or request spec compliance Mismatch against official JavaScript/TypeScript specification labels Apr 12, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
units/Goccia.Bytecode.Binary.pas (1)

164-167: Consider validating CookedValid length invariant to prevent silent stream corruption.

The writer serializes Length(Constant.CookedValid) booleans without writing the count explicitly, while the reader (lines 373-375) reads Length(CookedStrings) booleans. This relies on the invariant Length(CookedValid) = Length(CookedStrings).

However, AddConstantTemplateObject in Goccia.Bytecode.Chunk.pas (lines 330-332) only validates Length(ACookedStrings) <> Length(ARawStrings) but does not validate CookedValid length. If the invariant is ever violated (e.g., parser bug), the .gbc stream will be silently corrupted.

Consider adding validation in AddConstantTemplateObject:

if Length(ACookedValid) <> Length(ACookedStrings) then
  raise Exception.Create(
    'Template payload length mismatch: cooked validity vs cooked strings');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Bytecode.Binary.pas` around lines 164 - 167, Add a length-check
in AddConstantTemplateObject to ensure the CookedValid array matches the
CookedStrings array length and raise an exception if not; specifically verify
Length(ACookedValid) = Length(ACookedStrings) (or Length(ACookedValid) =
Length(ARawStrings) depending on existing invariants) and raise a descriptive
exception like "Template payload length mismatch: cooked validity vs cooked
strings" to prevent silent .gbc stream corruption when the writer (which writes
Length(Constant.CookedValid) booleans) and reader (which reads
Length(CookedStrings) booleans) diverge.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/language-restrictions.md`:
- Line 163: Line 163's inline code uses nested backticks (the tagged template
example `tag`...``) which breaks markdown rendering; update that inline span to
avoid nested backticks by using an alternate delimiter (e.g., wrap the entire
example in double backticks like ``tag`...`` or use an HTML <code> element) so
the tagged template example (`tag`...`) and String.raw remain correctly shown;
change the text containing "tag`...`" and "String.raw" to use the non-nested
inline code formatting.

In `@units/Goccia.Lexer.pas`:
- Around line 435-465: The surrogate-pair probe can advance the lexer even when
the trailing \uXXXX is incomplete or not a valid hex, desynchronizing
FCurrent/FColumn (see CodePoint check, Peek/PeekNext, SavedCurrent/SavedColumn,
Advance, HexStart, HexStr, IsValidHexString, LowSurrogate). Modify the lookahead
so that any path that does not result in a valid low surrogate (IsAtEnd during
the 4-char read, HexStr length ≠ 4, IsValidHexString = false, or low-surrogate
out of $DC00..$DFFF) restores FCurrent and FColumn from SavedCurrent/SavedColumn
before exiting the block; only keep the advanced state when CodePoint is
successfully combined into the paired code point.

---

Nitpick comments:
In `@units/Goccia.Bytecode.Binary.pas`:
- Around line 164-167: Add a length-check in AddConstantTemplateObject to ensure
the CookedValid array matches the CookedStrings array length and raise an
exception if not; specifically verify Length(ACookedValid) =
Length(ACookedStrings) (or Length(ACookedValid) = Length(ARawStrings) depending
on existing invariants) and raise a descriptive exception like "Template payload
length mismatch: cooked validity vs cooked strings" to prevent silent .gbc
stream corruption when the writer (which writes Length(Constant.CookedValid)
booleans) and reader (which reads Length(CookedStrings) booleans) diverge.
🪄 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: 9c280fb9-fb66-4a8f-a559-a0654dd7b556

📥 Commits

Reviewing files that changed from the base of the PR and between dfb9ffe and 801c446.

📒 Files selected for processing (11)
  • docs/language-restrictions.md
  • tests/language/expressions/string/tagged-templates.js
  • tests/language/expressions/string/template-string.js
  • units/Goccia.AST.Expressions.pas
  • units/Goccia.Bytecode.Binary.pas
  • units/Goccia.Bytecode.Chunk.pas
  • units/Goccia.Compiler.Expressions.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.Lexer.pas
  • units/Goccia.Parser.pas
  • units/Goccia.VM.pas

Comment thread docs/language-restrictions.md
Comment thread units/Goccia.Lexer.pas Outdated
Comment thread units/Goccia.Parser.pas Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 12, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 5208 5208
Tests Passed 5167 ✅ 5208 ✅
Tests Skipped 41 0
Tests Test Duration 358.5ms 374.2ms
Tests Lex 101.2ms 73.8ms
Tests Parse 128.9ms 138.2ms
Tests Compile 87.5ms
Tests Execute 377.1ms 411.6ms
Tests Engine Total 607.3ms 711.2ms
Benchmarks Total 364 364
Benchmarks Duration 10.20min 8.40min

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 12, 2026

Benchmark Results

364 benchmarks

Interpreted: 🟢 215 improved · 🔴 68 regressed · 81 unchanged · avg +2.5%
Bytecode: 🟢 52 improved · 🔴 213 regressed · 99 unchanged · avg -1.9%

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

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.

Address CodeRabbit review feedback:

- Lexer: guard surrogate pair lookahead against scanning past the
  closing backtick, and restore the cursor on every failure path
  (incomplete hex, invalid hex, non-surrogate low value)

- Parser: scope the untagged template SyntaxError to static segments
  only — split into segments first via SplitRawAtBoundaries so that
  malformed-looking sequences inside ${...} expression bodies do not
  cause false-positive rejections

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
units/Goccia.Parser.pas (1)

1230-1250: Prefer the shared code-point→UTF-8 helper here.

This hand-rolled UTF-8 encoding path now has to stay bit-for-bit aligned with the lexer and other decoders. Reusing the existing helper would reduce drift in exactly the area this PR is tightening up.

Based on learnings: when decoding Unicode escape sequences and converting them to UTF-8, prefer the existing helper implemented via CodePointToUTF8 that correctly combines UTF-16 surrogate pairs before UTF-8 encoding.

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

In `@units/Goccia.Parser.pas` around lines 1230 - 1250, Replace the manual UTF‑8
encoding block that appends bytes via ASB.AppendChar for variable CodePoint with
the shared helper CodePointToUTF8 so surrogate pairs are combined and encoding
logic stays consistent; locate the block that checks CodePoint ranges and remove
it, calling CodePointToUTF8 (the existing helper used by the lexer/decoders) to
produce/apply the UTF‑8 bytes (or append its returned string) instead of the
hand-rolled byte assembly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@units/Goccia.Parser.pas`:
- Around line 1166-1190: The CookUnicodeEscape function currently calls
StrToInt('$'+HexStr) which can raise on out-of-range hex payloads; replace this
with a safe conversion using TryStrToQWord (e.g., parse into a QWord temp),
return False when TryStrToQWord fails, then validate the numeric value (<=
$10FFFF) before casting into CodePoint (Cardinal) and continuing; update the
logic around the StrToInt call so malformed/overflowing braced escapes cause
CookUnicodeEscape to return False instead of raising (refer to
CookUnicodeEscape, CodePoint and HexStr identifiers).

---

Nitpick comments:
In `@units/Goccia.Parser.pas`:
- Around line 1230-1250: Replace the manual UTF‑8 encoding block that appends
bytes via ASB.AppendChar for variable CodePoint with the shared helper
CodePointToUTF8 so surrogate pairs are combined and encoding logic stays
consistent; locate the block that checks CodePoint ranges and remove it, calling
CodePointToUTF8 (the existing helper used by the lexer/decoders) to
produce/apply the UTF‑8 bytes (or append its returned string) instead of the
hand-rolled byte assembly.
🪄 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: 0027333f-a2a2-4105-9149-cab5f555964c

📥 Commits

Reviewing files that changed from the base of the PR and between 801c446 and 7c489d1.

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

Comment thread units/Goccia.Parser.pas
frostney and others added 2 commits April 12, 2026 15:06
Replace StrToInt('$' + HexStr) with TryStrToQWord in both the lexer's
ScanUnicodeEscapeForTemplate and the parser's CookUnicodeEscape for
the \u{...} braced path.  Arbitrarily long hex payloads like
\u{FFFFFFFF} now return False instead of raising an unhandled overflow
exception, so tagged templates correctly set cooked=undefined and
untagged templates emit SyntaxError.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hand-rolled UTF-8 byte assembly in CookUnicodeEscape with
a local CodePointToUTF8 helper, keeping the encoding logic in one
place and consistent with the same pattern in JSONParser and
Goccia.YAML.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney merged commit b5ab652 into main Apr 12, 2026
9 checks passed
@frostney frostney deleted the feature/template-literal-revision branch April 12, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature New feature or request spec compliance Mismatch against official JavaScript/TypeScript specification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Template Literal Revision: support cooked=undefined for malformed escapes in tagged templates

1 participant