Skip to content

Add bytecode REPL timing mode#200

Merged
frostney merged 6 commits intomainfrom
t3code/repl-bytecode-timing
Apr 7, 2026
Merged

Add bytecode REPL timing mode#200
frostney merged 6 commits intomainfrom
t3code/repl-bytecode-timing

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 7, 2026

Summary

  • Added --mode=bytecode to the REPL so it can run through the bytecode VM instead of the interpreter.
  • Added optional --timing output for bytecode REPL runs, reporting lex, parse, compile, execute, and total durations per line.
  • Wired the bytecode pipeline into the REPL loop, including JSX transform, parsing, compilation, module execution, and microtask draining.
  • Propagated top-level global-backed declarations through the compiler and VM so REPL bytecode execution can preserve let/const semantics for top-level bindings.
  • Updated the REPL usage text and AGENTS notes to document the new bytecode and timing modes.

Testing

  • Suggested check: ./build.pas REPL
  • Suggested check: ./build/REPL --mode=bytecode --timing
  • Suggested check: ./build.pas testrunner && ./build/TestRunner tests

Summary by CodeRabbit

  • New Features

    • REPL adds selectable modes (interpreted | bytecode) with an optional bytecode per-line timing; engine exposes detailed per-run timing.
  • Bug Fixes / Behavior

    • Top-level redeclaration validation now surfaces duplicate-declaration errors.
    • Global-backed top-level declarations affect bytecode/global-definition behavior.
  • Documentation

    • Command docs updated to document modes and timing option.

- Add `--mode=bytecode` and `--timing` output for REPL runs
- Teach the bytecode backend and VM to track top-level global bindings
- Update compiler emission for top-level globals, classes, destructuring, and enums
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e09ee30-2de1-457f-a713-42f3ae30b4cb

📥 Commits

Reviewing files that changed from the base of the PR and between de2fb20 and 8cc0b88.

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

📝 Walkthrough

Walkthrough

Adds a REPL --mode=interpreted|bytecode with optional per-line bytecode timing; implements global-backed top-level declarations across compiler/backend/VM with declaration-type-aware DefineGlobalBinding; and adds a top-level redeclaration validation pass.

Changes

Cohort / File(s) Summary
Docs
AGENTS.md
Annotate ./build/REPL as “(interpreted)” and document --mode=bytecode and --mode=bytecode --timing.
REPL entrypoint
REPL.dpr
Add --mode CLI flag; split into bytecode vs interpreted runtime paths. Bytecode path: optional JSX transform, explicit lex→parse→compile→run pipeline, per-phase timing, microtask draining, adjusted lifetimes and module tracking. Interpreted path: use engine Execute with updated timing sourcing.
Compiler flags & context
units/Goccia.Compiler.pas, units/Goccia.Compiler.Context.pas
Add GlobalBackedTopLevel boolean field/property and propagate it into TGocciaCompilationContext.
Top-level emission
units/Goccia.Compiler.Statements.pas, units/Goccia.Compiler.Expressions.pas
Emit OP_DEFINE_GLOBAL_CONST for top-level global-backed declarations with declaration-mode operand; add 8-bit name-index check; remove prior NewExpression global emit path.
Backend integration
units/Goccia.Engine.Backend.pas
Add GlobalBackedTopLevel property on bytecode backend; run pre-compile top-level redeclaration checks against interpreter global scope and forward flag to compiler.
VM and bytecode runtime
units/Goccia.VM.pas
Replace DefineGlobalValue with DefineGlobalBinding(name,value,declarationType); route OP_DEFINE_GLOBAL_CONST to use declaration operand; add syntax-error handler in bytecode exception flow; move Goccia.Scope.BindingMap to interface uses.
Engine timing & validation
units/Goccia.Engine.pas
Add LastTiming property and populate timing via FLastTiming; compute totals in finalization; call top-level redeclaration check after parsing.
Redeclaration pass
units/Goccia.Scope.Redeclaration.pas
New unit adding CheckTopLevelRedeclarations and helpers to detect/raise syntax errors for duplicate top-level declarations (including destructuring patterns).

Sequence Diagram

sequenceDiagram
    participant REPL as REPL Loop
    participant Lexer as Lexer
    participant Parser as Parser
    participant Compiler as Compiler
    participant Backend as Bytecode Backend
    participant VM as VM
    participant MTQ as Microtask Queue

    loop Each Input Line (bytecode mode)
        REPL->>Lexer: ScanTokens(input)
        Lexer-->>Parser: tokens
        Parser-->>Compiler: AST
        Compiler-->>Backend: module (GlobalBackedTopLevel flag)
        Backend->>VM: execute(module bytecode)
        VM-->>Backend: result
        Backend-->>REPL: formatted result (+timings)
        REPL->>MTQ: Drain microtask queue
        MTQ-->>REPL: processed
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped through tokens, parsed a dream, then stitched the bytecode seam,

I timed each hop and guarded scope, declared the globals with a beam.
Two REPL lanes now let me play — interpreted or bytecode way.
Microtasks cleared, errors found, the rabbit takes a gracious bow.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding bytecode REPL support with timing output mode.
Description check ✅ Passed The description includes all required sections: Summary explaining the changes with clear details, and Testing with suggested checks.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/repl-bytecode-timing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

- Route OP_DEFINE_GLOBAL_CONST through explicit let/const binding
- Remove unused global value helper and stale compiler emission
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: 4

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

4872-4880: Consider named constants for declaration-type flags.

The values 1 (let) and 2 (const) are opcode operand conventions. While the coding guidelines exempt trivial literals, introducing named constants like GLOBAL_DECL_LET = 1 and GLOBAL_DECL_CONST = 2 in Goccia.Bytecode.pas would improve readability and maintainability, especially for the compiler emission side.

💡 Suggested constant definitions (in Goccia.Bytecode.pas or similar)
const
  GLOBAL_DECL_VAR   = 0;  // legacy: DefineGlobalValue (upsert semantics)
  GLOBAL_DECL_LET   = 1;  // dtLet: strict redeclaration check
  GLOBAL_DECL_CONST = 2;  // dtConst: strict redeclaration check, immutable

Then in the VM:

 OP_DEFINE_GLOBAL_CONST:
 begin
   GlobalName := Template.GetConstantUnchecked(C).StringValue;
   case B of
-    1: DefineGlobalBinding(GlobalName, GetRegister(A), dtLet);
-    2: DefineGlobalBinding(GlobalName, GetRegister(A), dtConst);
+    GLOBAL_DECL_LET:   DefineGlobalBinding(GlobalName, GetRegister(A), dtLet);
+    GLOBAL_DECL_CONST: DefineGlobalBinding(GlobalName, GetRegister(A), dtConst);
   else
     DefineGlobalValue(GlobalName, GetRegister(A));
   end;
 end;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.VM.pas` around lines 4872 - 4880, The code uses magic numeric
flags (1,2) for global declaration types; introduce named constants (e.g.,
GLOBAL_DECL_VAR = 0, GLOBAL_DECL_LET = 1, GLOBAL_DECL_CONST = 2) in
Goccia.Bytecode.pas and replace the numeric checks in the VM branch that uses
Template.GetConstantUnchecked(C).StringValue with those constants so the case
becomes: GLOBAL_DECL_LET -> DefineGlobalBinding(..., dtLet), GLOBAL_DECL_CONST
-> DefineGlobalBinding(..., dtConst), otherwise -> DefineGlobalValue(...);
update any emitter/compiler code that emits these operand values to use the new
constants as well to keep encoding/decoding consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@REPL.dpr`:
- Around line 155-168: The timing for the "Lex" bucket currently starts before
StringListToLFText and TGocciaJSXTransformer.Transform, so JSX preprocessing is
being measured as lexing; adjust timing by moving the StartTime (and the
StartTime/lex timer boundary logic) to immediately before the call to
Lexer.ScanTokens (i.e., after StringListToLFText and after
TGocciaJSXTransformer.Transform when ggJSX is enabled), or alternatively add a
distinct timer bucket for the transform step (capture transform start/end around
TGocciaJSXTransformer.Transform and log it separately), and make the same change
for the other occurrence around lines 189-195 that also wraps
StringListToLFText/Transform/ScanTokens.
- Around line 181-187: ResultValue is a Pascal-held temporary that can be
collected during DrainQueue or FormatREPLValue; protect it by calling the
existing AddTempRoot(ResultValue) immediately after Backend.RunModule returns
and call RemoveTempRoot(ResultValue) after the WriteLn/FormatREPLValue is
finished (or if ResultValue is nil skip protection), and when triggering GC from
TGocciaMicrotaskQueue.Instance.DrainQueue use the protected
CollectIfNeeded(AProtect) path so the temp-root is honored; update the block
around ResultValue, DrainQueue and FormatREPLValue to add/remove the temp root
and use CollectIfNeeded(AProtect).

In `@units/Goccia.Compiler.Statements.pas`:
- Around line 571-572: Move the global redeclaration preflight so it runs before
evaluating RHS/initializer: when IsTopLevelGlobalBacked is true, call
EmitGlobalDefine (or an equivalent "predeclare" path using ACtx, Slot,
Info.Name, AStmt.IsConst) before executing the RHS/class
body/destructuring/enum, and then perform the actual initialization after
evaluation completes; update the same pattern at the other occurrences noted
(around the locations corresponding to the blocks at lines ~2074-2075,
~2273-2278, ~2326-2327) so bindings are rejected up-front and side effects in
evaluation cannot run before DefineGlobalBinding rejects redeclarations.

In `@units/Goccia.VM.pas`:
- Around line 2433-2439: DefineGlobalBinding calls DefineLexicalBinding which
can raise TGocciaSyntaxError (e.g. during OP_DEFINE_GLOBAL_CONST redeclaration),
but the VM loop only handles
TGocciaTypeError/TGocciaReferenceError/TGocciaRuntimeError; add a
TGocciaSyntaxError handler in the VM execution loop (alongside the existing
exception handlers) that converts the TGocciaSyntaxError into an
EGocciaBytecodeThrow carrying a SyntaxError object (matching the pattern used
for other errors), or alternatively update DefineGlobalBinding to pre-check for
existing global bindings and raise a caught error type instead; reference
TGocciaSyntaxError, EGocciaBytecodeThrow, DefineGlobalBinding,
DefineLexicalBinding and OP_DEFINE_GLOBAL_CONST when applying the change.

---

Nitpick comments:
In `@units/Goccia.VM.pas`:
- Around line 4872-4880: The code uses magic numeric flags (1,2) for global
declaration types; introduce named constants (e.g., GLOBAL_DECL_VAR = 0,
GLOBAL_DECL_LET = 1, GLOBAL_DECL_CONST = 2) in Goccia.Bytecode.pas and replace
the numeric checks in the VM branch that uses
Template.GetConstantUnchecked(C).StringValue with those constants so the case
becomes: GLOBAL_DECL_LET -> DefineGlobalBinding(..., dtLet), GLOBAL_DECL_CONST
-> DefineGlobalBinding(..., dtConst), otherwise -> DefineGlobalValue(...);
update any emitter/compiler code that emits these operand values to use the new
constants as well to keep encoding/decoding consistent.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f27b729-6c52-46d4-a7ba-41861e083c57

📥 Commits

Reviewing files that changed from the base of the PR and between 0d9870c and 009f234.

📒 Files selected for processing (7)
  • AGENTS.md
  • REPL.dpr
  • units/Goccia.Compiler.Context.pas
  • units/Goccia.Compiler.Statements.pas
  • units/Goccia.Compiler.pas
  • units/Goccia.Engine.Backend.pas
  • units/Goccia.VM.pas

Comment thread REPL.dpr Outdated
Comment on lines +155 to +168
StartTime := GetNanoseconds;
SourceText := StringListToLFText(Source);

if ggJSX in TGocciaEngine.DefaultGlobals then
begin
JSXResult := TGocciaJSXTransformer.Transform(SourceText);
SourceText := JSXResult.Source;
JSXResult.SourceMap.Free;
end;

Lexer := TGocciaLexer.Create(SourceText, REPL_FILE_NAME);
try
Tokens := Lexer.ScanTokens;
LexEnd := GetNanoseconds;
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 7, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

The reported Lex time currently includes JSX preprocessing.

StartTime is taken before StringListToLFText and TGocciaJSXTransformer.Transform, but the first bucket is labeled Lex. On JSX inputs that overstates lexing and makes the new phase breakdown misleading. Start the lex timer immediately before ScanTokens, or report transform/preprocess as its own bucket.

Also applies to: 189-195

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

In `@REPL.dpr` around lines 155 - 168, The timing for the "Lex" bucket currently
starts before StringListToLFText and TGocciaJSXTransformer.Transform, so JSX
preprocessing is being measured as lexing; adjust timing by moving the StartTime
(and the StartTime/lex timer boundary logic) to immediately before the call to
Lexer.ScanTokens (i.e., after StringListToLFText and after
TGocciaJSXTransformer.Transform when ggJSX is enabled), or alternatively add a
distinct timer bucket for the transform step (capture transform start/end around
TGocciaJSXTransformer.Transform and log it separately), and make the same change
for the other occurrence around lines 189-195 that also wraps
StringListToLFText/Transform/ScanTokens.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Not a bug — this matches the timing convention in both Engine.Execute and ScriptLoader's ParseSource. All three measure Lex = LexEnd - StartTime where StartTime is captured before JSX transform. This is consistent across every CLI (ScriptLoader, TestRunner, BenchmarkRunner) and the engine itself. The JSX pre-pass is part of the "source → tokens" pipeline, so including it in the Lex bucket is intentional.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@frostney, understood — thanks for the clear explanation. I'll note this convention for future reviews.


✏️ Learnings added
Learnt from: frostney
Repo: frostney/GocciaScript PR: 200
File: REPL.dpr:0-0
Timestamp: 2026-04-07T17:12:07.324Z
Learning: Repo: frostney/GocciaScript — REPL.dpr, ScriptLoader, TestRunner, BenchmarkRunner, and TGocciaEngine.Execute all share the same timing convention: `StartTime` is captured before JSX transformation (`TGocciaJSXTransformer.Transform`) and `StringListToLFText`, and `LexEnd` is captured after `ScanTokens`. The "Lex" bucket (`LexEnd - StartTime`) intentionally includes the JSX pre-pass because the JSX transform is considered part of the "source → tokens" pipeline. Do not flag this as a bug or suggest moving `StartTime` to after the JSX transform.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: frostney/GocciaScript PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T21:38:39.013Z
Learning: Applies to **/*{Bytecode,VM}*.pas : Bytecode mode uses the same runtime objects as the interpreter. Use `Goccia.Bytecode*` and `Goccia.VM*` names in new code. Use `.gbc`, not `.sbc`. WASM emission removed pre-1.0.

Learnt from: CR
Repo: frostney/GocciaScript PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T21:38:39.013Z
Learning: Applies to **/*JSX*.pas : JSX is handled by standalone pre-pass transformer (`Goccia.JSX.Transformer.pas`) converting JSX syntax to `createElement` calls. Transformer generates internal source map for correct error reporting. Zero overhead when `ggJSX` not enabled.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 107
File: units/Goccia.Runtime.Operations.pas:211-212
Timestamp: 2026-03-23T12:37:30.936Z
Learning: Repo: frostney/GocciaScript — units/Goccia.Runtime.Operations.pas (PR `#107`)
Learning: In TGocciaRuntimeOperations.MarkExternalRoots, entries in FModuleCache (cached module namespace/JSON records) must be traversed and their TSouffleValue references marked as external roots to survive GC. This behavior was added in commit 36f69cc; do not re‑flag missing FModuleCache rooting unless the logic regresses.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 147
File: units/Goccia.VM.pas:3042-3084
Timestamp: 2026-03-31T18:38:23.367Z
Learning: Repo: frostney/GocciaScript — units/Goccia.VM.pas (PR `#147`)
As of commit 457e712: (1) TGocciaVM.GetPropertyValue performs own-only private static accessor lookup on '#…' for TGocciaClassValue and throws TypeError on class-brand misses; (2) TGocciaVM.SetPropertyValue avoids inherited private accessor resolution, but still allows raw private static writes via SetRawPrivateValue to support current class initialization that emits OP_SET_PROP_CONST for private static fields. Planned follow-up is to add dedicated opcodes to define private static fields at class-declaration time and then make SetPropertyValue strict (throw on brand miss with no fallback). Do not re-flag the current fallback until those opcodes exist.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 66
File: units/ScopeMap.pas:0-0
Timestamp: 2026-03-17T22:18:02.668Z
Learning: Repo: frostney/GocciaScript — ScopeMap.pas (units/ScopeMap.pas) was deleted (commit d99cf96) after profiling showed TScopeMap's linear-scan IndexOf was ~2.7× slower than hash-based lookup (CreateBridgedContext samples rose from 24% to 51%). Scope bindings now use TOrderedStringMap<TLexicalBinding> (DJB2 hash, O(1) per level); scope chain walking is done via recursive TGocciaScope-level methods (Contains/GetLexicalBinding) rather than a parent-pointer chain inside the map. Do not flag TScopeMap or its Parent property in future reviews — the type no longer exists.

Learnt from: CR
Repo: frostney/GocciaScript PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-08T04:38:51.695Z
Learning: Applies to **/*.pas : Type annotations in GocciaScript (parsed from source) are preserved as raw strings on AST nodes. In interpreted mode, annotations are ignored at runtime. In bytecode mode, the compiler uses annotations and inferred types to emit `OP_CHECK_TYPE` guards and typed opcodes — reassignment to an incompatible type throws `TypeError` at runtime.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 105
File: docs/optimization-log.md:45-46
Timestamp: 2026-03-18T15:34:27.613Z
Learning: Applies to units/Goccia.Compiler*.pas|units/Goccia.Runtime.Operations.pas|Goccia.Compiler.ExtOps.pas|Goccia.Compiler.Statements.pas: As of PR `#75`, `GOCCIA_EXT_EVAL_CLASS`, `EvaluateClassByIndex`, and `FPendingClasses` are completely removed from the codebase. Decorated classes are NOT deferred to the interpreter. ALL classes (with or without decorators) are compiled natively via `GOCCIA_EXT_BEGIN_DECORATORS` / `APPLY_ELEMENT_DECORATOR` / `FINISH_DECORATORS` in the bytecode pipeline. Do NOT flag decorator compilation as a deferred/bridge path.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 137
File: AGENTS.md:102-109
Timestamp: 2026-03-30T07:34:23.743Z
Learning: In frostney/GocciaScript (units/Goccia.VM*.pas), the bytecode VM register file uses a tagged `TGocciaRegister` type, not `TGocciaValue` directly. Hot scalar kinds (undefined, null, hole, booleans, integers, floats) stay unboxed in registers; object/runtime boundaries materialize `TGocciaValue` instances when needed. Do NOT describe the VM register file as storing `TGocciaValue` directly in reviews.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 87
File: units/Goccia.Runtime.Operations.pas:3383-3387
Timestamp: 2026-03-17T16:28:05.526Z
Learning: Repo: frostney/GocciaScript — Follow-up tracking
Learning: Splitting the blueprint bridge caches (value bridge vs construct bridge) is tracked in issue `#99`. Until that lands, sharing FBlueprintBridgeCache between UnwrapToGocciaValue (BlueprintToClassValue) and Construct (ConvertBlueprintToClassValue) should not block PRs; reference `#99` instead of re-flagging.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 60
File: units/Goccia.Runtime.Operations.pas:8694-8696
Timestamp: 2026-03-10T18:05:46.926Z
Learning: Repo: frostney/GocciaScript PR: 60 — units/Goccia.Runtime.Operations.pas: The GC rooting of FArrayBridgeReverse (marking values in MarkExternalRoots and keys in MarkWrappedGocciaValues) is intentional to preserve array identity across async/microtask boundaries (e.g., Promise.resolve(arr).then(v => v === arr)). Do not prune this map during normal clearing; per-file GC.Collect in runners bounds cross-file memory growth.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 58
File: units/Goccia.Runtime.Operations.pas:5959-6132
Timestamp: 2026-03-10T11:17:57.064Z
Learning: Repo: frostney/GocciaScript PR: 58
Context: units/Goccia.Runtime.Operations.pas (BridgePromiseStatic, NativePromiseStaticResolve, NativePromiseStaticReject) and interpreter Promise statics in Goccia.Builtins.GlobalPromise.pas
Learning: GocciaScript’s Promise statics are intentionally NOT species/subclass-aware. Interpreter-level implementations (PromiseAll, PromiseRace, PromiseAllSettled, PromiseAny, withResolvers, try, resolve, reject) construct plain TGocciaPromiseValue via WrapAsPromise and do not use the call receiver (AThisValue). The Souffle VM bridge mirrors this by not forwarding AReceiver and by constructing TGocciaPromiseValue directly where applicable. Do not flag missing receiver-based construction for Promise statics.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 109
File: units/Goccia.Runtime.Operations.pas:2797-2828
Timestamp: 2026-03-23T15:57:21.755Z
Learning: Repo frostney/GocciaScript — PR `#109`: Native Map/Set scaffolding
- Current behavior: ToSouffleValue wraps TGocciaMapValue/TGocciaSetValue as TGocciaWrappedValue; TGocciaSouffleMap/TGocciaSouffleSet instances are not produced in normal execution.
- Pre-wired fast paths: GetProperty, GetIterator, and IteratorNext recognize TGocciaSouffleMap/TGocciaSouffleSet and their iterator types; these are staged for future activation.
- Deferred work: When Construct is updated to produce native collection types, add corresponding fast paths in HasProperty, SetProperty, and DeleteProperty.

Comment thread REPL.dpr Outdated
Comment thread units/Goccia.Compiler.Statements.pas
Comment thread units/Goccia.VM.pas
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 7 improved · 🔴 226 regressed · 41 unchanged · avg -5.1%
Bytecode: 🟢 7 improved · 🔴 228 regressed · 39 unchanged · avg -6.4%

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

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 3701 3701
Tests Passed 3660 ✅ 3701 ✅
Tests Skipped 41 0
Tests Test Duration 212.5ms 183.7ms
Tests Lex 68.8ms 47.3ms
Tests Parse 91.0ms 91.8ms
Tests Compile 54.3ms
Tests Execute 226.7ms 208.5ms
Tests Engine Total 386.4ms 401.9ms
Benchmarks Total 274 274
Benchmarks Duration 7.47min 6.39min

Measured on ubuntu-latest x64.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
units/Goccia.Compiler.Statements.pas (1)

569-570: ⚠️ Potential issue | 🟠 Major

Predeclare the binding before any initializer/class/enum work runs.

Line 569, Line 2072, Line 2271, and Line 2324 still emit the global define only after user code has already executed. In bytecode REPL mode, redeclaring an existing top-level let/const can therefore run RHS side effects, decorators, extends expressions, destructuring getters, or enum member initializers before DefineGlobalBinding rejects the redeclaration. This still breaks lexical declaration semantics and needs a predeclare/preflight step before evaluation.

Also applies to: 2072-2073, 2271-2275, 2324-2325

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

In `@units/Goccia.Compiler.Statements.pas` around lines 569 - 570, The current
code calls EmitGlobalDefine(ACtx, Slot, Info.Name, AStmt.IsConst) only after
running initializer/class/enum work (guarded by IsTopLevelGlobalBacked), which
allows RHS side effects to run before the binding is rejected; to fix, move a
pre-declaration step so that for top-level, global-backed lexical declarations
you call EmitGlobalDefine (or a new DefineGlobalBindingPredeclare helper) before
any initializer, decorator, extends expression, destructuring getter, or enum
member initializer logic is executed in the same code paths that currently check
IsTopLevelGlobalBacked and then emit later; update all sites that currently call
EmitGlobalDefine after evaluation (the occurrences using
IsTopLevelGlobalBacked/EmitGlobalDefine) to perform the predeclare first and
then proceed with initializer/evaluation, preserving the existing
EmitGlobalDefine semantics for final binding setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@units/Goccia.Compiler.Statements.pas`:
- Around line 569-570: The current code calls EmitGlobalDefine(ACtx, Slot,
Info.Name, AStmt.IsConst) only after running initializer/class/enum work
(guarded by IsTopLevelGlobalBacked), which allows RHS side effects to run before
the binding is rejected; to fix, move a pre-declaration step so that for
top-level, global-backed lexical declarations you call EmitGlobalDefine (or a
new DefineGlobalBindingPredeclare helper) before any initializer, decorator,
extends expression, destructuring getter, or enum member initializer logic is
executed in the same code paths that currently check IsTopLevelGlobalBacked and
then emit later; update all sites that currently call EmitGlobalDefine after
evaluation (the occurrences using IsTopLevelGlobalBacked/EmitGlobalDefine) to
perform the predeclare first and then proceed with initializer/evaluation,
preserving the existing EmitGlobalDefine semantics for final binding setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b04b7d3c-b942-4f71-9fae-893aaf688ae7

📥 Commits

Reviewing files that changed from the base of the PR and between 009f234 and 0f2b872.

📒 Files selected for processing (3)
  • units/Goccia.Compiler.Expressions.pas
  • units/Goccia.Compiler.Statements.pas
  • units/Goccia.VM.pas
💤 Files with no reviewable changes (1)
  • units/Goccia.Compiler.Expressions.pas
🚧 Files skipped from review as they are similar to previous changes (1)
  • units/Goccia.VM.pas

frostney added 2 commits April 7, 2026 17:04
- Preserve timing output when execution raises
- Store last script timing on the engine for REPL display
- Check global scope bindings before running scripts or compiling bytecode
- Surface duplicate top-level declarations from destructuring, classes, enums, and variables
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.

🧹 Nitpick comments (1)
units/Goccia.Engine.Backend.pas (1)

95-180: Consider extracting shared redeclaration logic to avoid duplication.

CollectDestructuringPatternNames and CheckTopLevelRedeclarations are nearly identical to CollectPatternRedeclarations and CheckRedeclarations in units/Goccia.Engine.pas. This duplication increases maintenance burden and risks divergence.

Consider extracting these to a shared unit (e.g., Goccia.Redeclaration.pas) and reusing in both TGocciaEngine.Execute and TGocciaBytecodeBackend.CompileToModule.

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

In `@units/Goccia.Engine.Backend.pas` around lines 95 - 180, The two functions
here (CollectDestructuringPatternNames and CheckTopLevelRedeclarations)
duplicate logic present as CollectPatternRedeclarations and CheckRedeclarations
in units/Goccia.Engine.pas; extract the shared redeclaration traversal and
error-raising logic into a single reusable unit (e.g., TGocciaRedeclaration or
unit Goccia.Redeclaration) that exposes functions like
CollectPatternRedeclarations/CheckRedeclarations (or generic names
CollectDestructuringPatternNames/CheckTopLevelRedeclarations moved) and then
replace the local implementations in both TGocciaEngine.Execute and
TGocciaBytecodeBackend.CompileToModule to call the shared routines, updating
uses clauses and preserving the exact error messages and parameter lists
(APattern, AScope, ASourcePath, TGocciaProgram, etc.) so behavior stays
identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@units/Goccia.Engine.Backend.pas`:
- Around line 95-180: The two functions here (CollectDestructuringPatternNames
and CheckTopLevelRedeclarations) duplicate logic present as
CollectPatternRedeclarations and CheckRedeclarations in units/Goccia.Engine.pas;
extract the shared redeclaration traversal and error-raising logic into a single
reusable unit (e.g., TGocciaRedeclaration or unit Goccia.Redeclaration) that
exposes functions like CollectPatternRedeclarations/CheckRedeclarations (or
generic names CollectDestructuringPatternNames/CheckTopLevelRedeclarations
moved) and then replace the local implementations in both TGocciaEngine.Execute
and TGocciaBytecodeBackend.CompileToModule to call the shared routines, updating
uses clauses and preserving the exact error messages and parameter lists
(APattern, AScope, ASourcePath, TGocciaProgram, etc.) so behavior stays
identical.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a3e1fbda-cc47-46e5-a947-cb37fb36e8c5

📥 Commits

Reviewing files that changed from the base of the PR and between 0f2b872 and abfc8d9.

📒 Files selected for processing (3)
  • REPL.dpr
  • units/Goccia.Engine.Backend.pas
  • units/Goccia.Engine.pas

- Move program and pattern redeclaration logic into `Goccia.Scope.Redeclaration`
- Update engine backend to reuse the shared checker
- Keep bytecode and interpreter redeclaration handling aligned
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.

🧹 Nitpick comments (1)
units/Goccia.Scope.Redeclaration.pas (1)

31-38: Line/column information lost for destructuring pattern errors.

When a redeclaration is detected in a destructuring pattern, the error is raised with 0, 0 for line/column. If TGocciaDestructuringPattern or its subclasses have position information available, consider propagating it for better error diagnostics.

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

In `@units/Goccia.Scope.Redeclaration.pas` around lines 31 - 38, The redeclaration
error for TGocciaIdentifierDestructuringPattern is being raised with hardcoded
line/column 0,0; update the TGocciaSyntaxError.Create call to pass the actual
source position from the pattern (e.g. use position/line/column properties
available on TGocciaDestructuringPattern or
TGocciaIdentifierDestructuringPattern) instead of 0,0, falling back to the
existing ASourcePath and the current 0,0 only if no position info exists; modify
the code near AScope.ContainsOwnLexicalBinding and the TGocciaSyntaxError.Create
invocation to read and propagate the pattern's position fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@units/Goccia.Scope.Redeclaration.pas`:
- Around line 31-38: The redeclaration error for
TGocciaIdentifierDestructuringPattern is being raised with hardcoded line/column
0,0; update the TGocciaSyntaxError.Create call to pass the actual source
position from the pattern (e.g. use position/line/column properties available on
TGocciaDestructuringPattern or TGocciaIdentifierDestructuringPattern) instead of
0,0, falling back to the existing ASourcePath and the current 0,0 only if no
position info exists; modify the code near AScope.ContainsOwnLexicalBinding and
the TGocciaSyntaxError.Create invocation to read and propagate the pattern's
position fields.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57410ba2-1119-4ce5-8452-5005006785b2

📥 Commits

Reviewing files that changed from the base of the PR and between abfc8d9 and de2fb20.

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

- Root REPL results during timing so microtask draining cannot collect them early
- Route TGocciaSyntaxError through bytecode exception handlers as SyntaxError
@frostney frostney merged commit 23c908a into main Apr 7, 2026
9 checks passed
@frostney frostney deleted the t3code/repl-bytecode-timing branch April 7, 2026 17:35
@frostney frostney added the new feature New feature or request label Apr 9, 2026
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant