Skip to content

Align error suggestions across builtins and runners#308

Merged
frostney merged 1 commit intomainfrom
t3code/align-error-suggestions
Apr 16, 2026
Merged

Align error suggestions across builtins and runners#308
frostney merged 1 commit intomainfrom
t3code/align-error-suggestions

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 15, 2026

Summary

  • Added suggestion-aware error formatting throughout the runners, REPL, application entrypoint, and VM exception paths so thrown values now surface consistent hints.
  • Replaced many ad hoc builtin error strings with centralized error message and suggestion constants across core objects, collections, promises, proxies, temporal APIs, and utility types.
  • Introduced shared error message and suggestion modules to standardize diagnostics and reduce divergence between call sites.
  • Added build/ppas.sh to support assembling and linking the generated Pascal artifacts on macOS.

Testing

  • Concrete check: verify affected runners still compile with ./build.pas testrunner && ./build.pas loader && ./build.pas repl && ./build.pas benchmarkrunner.
  • Concrete check: run the test suite with ./build/TestRunner tests and confirm suggestion text appears in thrown-value error output where expected.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa684054-9a32-4991-9991-a0c5df5081b0

📥 Commits

Reviewing files that changed from the base of the PR and between 7581e1e and 9c6134d.

📒 Files selected for processing (81)
  • .gitignore
  • BenchmarkRunner.dpr
  • REPL.dpr
  • ScriptLoader.dpr
  • TestRunner.dpr
  • units/Goccia.Application.pas
  • units/Goccia.Builtins.DisposableStack.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.GlobalArrayBuffer.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Builtins.Temporal.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Error.Suggestions.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Scope.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.URI.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.Iterator.Zip.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.SymbolValue.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Values.TemporalPlainDate.pas
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas

📝 Walkthrough

Walkthrough

Adds centralized error message and suggestion resources, extends thrown-value and error-helper APIs to carry suggestion metadata, threads suggestion data into error-detail formatting and tooling output, and replaces many hardcoded error strings with standardized SError/SSuggest pairs across runtime, builtins, values, and tooling.

Changes

Cohort / File(s) Summary
Error infra & helpers
units/Goccia.Error.Messages.pas, units/Goccia.Error.Suggestions.pas, units/Goccia.Values.Error.pas, units/Goccia.Values.ErrorHelper.pas, units/Goccia.Error.Detail.pas
Add resourcestring catalogs for messages and suggestions; add Suggestion field and constructor overload to TGocciaThrowValue; add error-helper overloads that accept a suggestion; extend FormatThrowDetail to accept optional suggestion.
Runners & tooling
BenchmarkRunner.dpr, REPL.dpr, ScriptLoader.dpr, TestRunner.dpr
Propagate TGocciaThrowValue.Suggestion into exception handlers and pass suggestion into FormatThrowDetail and error/file output paths.
Core runtime / evaluator / VM / scope
units/Goccia.Evaluator.pas, units/Goccia.VM.pas, units/Goccia.Scope.pas, units/Goccia.Application.pas, units/Goccia.Evaluator.*.pas
Replace many inline literal error messages with SError* templates and SSuggest* codes; attach suggestion args to throws and add corresponding uses imports.
Builtins & parsing libraries
units/Goccia.Builtins.*.pas (GlobalArray, GlobalArrayBuffer, GlobalFFI, GlobalMap, GlobalObject, GlobalPromise, GlobalProxy, GlobalReflect, GlobalRegExp, GlobalString, GlobalSymbol, JSON/JSON5/JSONL, YAML, TOML, Performance, Semver, Temporal, TestAssertions, DisposableStack, Globals, etc.)
Standardize validation/argument/receiver errors to use centralized SError* and SSuggest* constants; replace string concatenation with Format(...) where required and update uses.
Values, iterators & typed APIs
units/Goccia.Values.*.pas (Array, ArrayBufferValue, TypedArrayValue, PromiseValue, ObjectValue, SymbolValue, URL*, TextEncoder/Decoder, Iterator*, FFILibrary/Pointer, ProxyValue, SharedArrayBuffer, Uint8ArrayEncoding, URLSearchParams, MapValue, etc.)
Migrate hardcoded throw messages to SError*/SSuggest*, add formatting overloads (e.g., TGocciaArrayValue.ThrowError), and update TypeError/RangeError/SyntaxError call sites and imports.
Utilities / misc / config
units/Goccia.Utils.Arrays.pas, units/Goccia.ImportMeta.pas, units/Goccia.URI.pas, units/Goccia.DisposalTracker.pas, units/Goccia.Builtins.DisposableStack.pas, .gitignore
Replace literal errors with standardized templates/suggestions in utility modules; add new error imports; update .gitignore to ignore two build scripts.

Sequence Diagram(s)

sequenceDiagram
    participant ThrowSite as Throwing code
    participant ErrorHelper as ErrorHelper
    participant ThrowValue as TGocciaThrowValue
    participant Runner as Runner/REPL/Loader
    participant Formatter as FormatThrowDetail
    participant Output as Console/File

    ThrowSite->>ErrorHelper: ThrowTypeError(..., ASuggestion?)
    ErrorHelper->>ThrowValue: Create(ErrorObject, ASuggestion)
    ThrowValue-->>Runner: exception propagates
    Runner->>Formatter: FormatThrowDetail(Value, File, Source, UseColor, ASuggestion)
    Formatter-->>Runner: formatted string (includes suggestion when present)
    Runner->>Output: WriteLn / write file
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • PR #205 — propagates suggestion metadata through throw types and formatting; overlaps runner/REPL/loader changes.
  • PR #23 — touches error construction/formatting helpers (CreateErrorObject/throw plumbing); overlaps Values.Error/Values.ErrorHelper modifications.
  • PR #266 — modifies GlobalArrayBuffer constructor/length validation; overlaps ArrayBuffer-related error-message/suggestion updates.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Align error suggestions across builtins and runners' is concise, specific, and accurately summarizes the main objective of the changeset: standardizing error suggestions throughout the codebase.
Description check ✅ Passed The pull request description covers the main changes and provides specific testing instructions, but is missing the standard PR template structure with explicit checkboxes and documentation updates.

✏️ 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 the internal Refactoring, CI, tooling, cleanup label Apr 15, 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: 16

Caution

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

⚠️ Outside diff range comments (2)
units/Goccia.Values.Iterator.Lazy.pas (1)

552-557: ⚠️ Potential issue | 🟠 Major

Preserve the intended TypeError when iterator close throws.

On Line 555 and Line 615, direct FSourceIterator.Close can throw and mask the subsequent ThrowTypeError(...). Use the local preservation helper so the flatMap contract error remains the surfaced exception.

🛠️ Proposed fix
@@
-      FSourceIterator.Close;
+      CloseIteratorPreservingOriginalError(FSourceIterator);
       ThrowTypeError(SErrorIteratorFlatMapMustReturnIterable, SSuggestIteratorFlatMapCallable);
@@
-      FSourceIterator.Close;
+      CloseIteratorPreservingOriginalError(FSourceIterator);
       ThrowTypeError(SErrorIteratorFlatMapMustReturnIterable, SSuggestIteratorFlatMapCallable);

Also applies to: 612-617

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

In `@units/Goccia.Values.Iterator.Lazy.pas` around lines 552 - 557, The code
currently calls FSourceIterator.Close directly before ThrowTypeError which can
raise and mask the intended TypeError from ThrowTypeError; update the two
locations (around ResolveIterator / FInnerIterator and the other block at
612-617) to use the local exception-preservation helper (i.e., save the current
exception, call FSourceIterator.Close within a try/finally that restores the
saved exception) so that any exception from Close does not replace the
ThrowTypeError(SErrorIteratorFlatMapMustReturnIterable,
SSuggestIteratorFlatMapCallable) error; ensure the helper wraps the Close call
around FSourceIterator.Close while still allowing proper cleanup.
units/Goccia.Error.Detail.pas (1)

120-133: ⚠️ Potential issue | 🟠 Major

Fallback formatting path drops ASuggestion

ASuggestion is only used in the source-context branch (Line 115-117). When fallback rendering is used, suggestions are silently lost, so diagnostics become inconsistent.

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

In `@units/Goccia.Error.Detail.pas` around lines 120 - 133, The fallback branch
that builds Result for non-source-context errors drops ASuggestion, so when
TGocciaObjectValue fallback or the final else set Result (using StackValue or
AThrown.ToStringLiteral.Value) they must also append the suggestion exactly as
the source-context path does; update the fallback assignments of Result to
include ASuggestion when provided (e.g., check ASuggestion<>'' and concatenate
it with the existing Result string) so diagnostics remain consistent with the
source-context branch.
🧹 Nitpick comments (12)
units/Goccia.Values.TemporalInstant.pas (1)

64-64: Consider centralizing the remaining ad hoc Temporal.Instant TypeError strings.

Line 64 and Line 90 still construct hardcoded messages directly; migrating these to shared SError* constants would fully align this unit with the PR’s standardization direction.

Based on learnings: Applies to **/*.pas : Use split constant units for runtime constants instead of hardcoded string literals: Goccia.Constants.PropertyNames for property names, Goccia.Constants.TypeNames for type names, Goccia.Constants.ErrorNames for error names, Goccia.Constants.ConstructorNames for constructor names, and Goccia.Constants.SymbolNames for symbol names.

Also applies to: 90-90

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

In `@units/Goccia.Values.TemporalInstant.pas` at line 64, Replace the two
hardcoded Temporal.Instant TypeError messages that call ThrowTypeError (the
instance using AMethod + ' called on non-Instant', SSuggestTemporalFromArg and
the similar message at the other occurrence) with shared string constants
located in the centralized constants unit (e.g., Goccia.Constants.ErrorNames or
a new SErrorTemporalInstant constant), then update the ThrowTypeError calls to
use those constants; locate the usages by searching for ThrowTypeError and the
concatenation pattern (AMethod + ' called on non-Instant') and replace the
literal strings with the new SError* constant names so this unit aligns with the
project's standardized error constants.
units/Goccia.Values.TemporalPlainYearMonth.pas (1)

103-104: Move remaining PlainYearMonth literal error messages into shared constants.

Line 103, Line 161/164, and Line 171 still build ad-hoc message strings; migrating those to Goccia.Error.Messages would complete alignment in this file.

Based on learnings: Applies to **/*.pas : Use split constant units for runtime constants instead of hardcoded string literals, especially for runtime error text.

Also applies to: 161-165, 171-172

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

In `@units/Goccia.Values.TemporalPlainYearMonth.pas` around lines 103 - 104,
Replace the ad-hoc PlainYearMonth error message strings used in ThrowTypeError
and other places with shared constants from the Goccia.Error.Messages unit:
define appropriate constants (e.g., ErrNotPlainYearMonth,
ErrPlainYearMonthSuggestFromArg, etc.) in Goccia.Error.Messages and import that
unit into units/Goccia.Values.TemporalPlainYearMonth.pas, then update the calls
(ThrowTypeError(AMethod + ' called on non-PlainYearMonth',
SSuggestTemporalFromArg), and the messages at the locations referenced around
the TGocciaTemporalPlainYearMonthValue checks at lines ~161–165 and ~171–172) to
use those constants instead of string literals so all runtime error text is
centralized.
units/Goccia.Values.TemporalZonedDateTime.pas (1)

115-116: Replace the remaining ad-hoc Temporal error strings in changed paths.

Line 115, Line 152, and Line 263 still construct literal error text instead of using Goccia.Error.Messages constants, which leaves this unit partially outside the centralized error catalog.

Based on learnings: Applies to **/*.pas : Use split constant units for runtime constants instead of hardcoded string literals (Goccia.Constants.*), and keep runtime error construction standardized via shared helpers/resources.

Also applies to: 152-153, 263-264

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

In `@units/Goccia.Values.TemporalZonedDateTime.pas` around lines 115 - 116,
Replace the ad-hoc literal error text used when validating ZonedDateTime values
by using the centralized error/messages constants and helper: instead of calling
ThrowTypeError(AMethod + ' called on non-ZonedDateTime',
SSuggestTemporalFromArg) and returning TGocciaTemporalZonedDateTimeValue(AValue)
with hardcoded strings, assemble the error text using the appropriate constant
from Goccia.Error.Messages (or the split runtime constants in
Goccia.Constants.*) and pass that into ThrowTypeError (or the shared
error-construction helper) so all messages in TGocciaTemporalZonedDateTimeValue
validation use the centralized catalog; update the other occurrences mentioned
(the other checks around TGocciaTemporalZonedDateTimeValue) to follow the same
pattern.
units/Goccia.Values.TemporalPlainTime.pas (1)

77-78: Centralize the two remaining hardcoded PlainTime error strings.

Line 77 and Line 118 still compose literal runtime error text; switching these to Goccia.Error.Messages constants would fully align this unit with the new diagnostic model.

Based on learnings: Applies to **/*.pas : Use split constant units for runtime constants instead of hardcoded string literals, including runtime diagnostic text.

Also applies to: 118-119

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

In `@units/Goccia.Values.TemporalPlainTime.pas` around lines 77 - 78, Two
remaining hardcoded PlainTime diagnostic messages in this unit should be
replaced with centralized constants from Goccia.Error.Messages: replace the
literal string used in the ThrowTypeError call that builds AMethod + ' called on
non-PlainTime' (the call immediately before the
TGocciaTemporalPlainTimeValue(AValue) cast) and the other literal used around
lines 118-119 with appropriate constants from Goccia.Error.Messages (e.g.,
PlainTimeCalledOnNonPlainTime and the corresponding suggestion constant instead
of SSuggestTemporalFromArg if a specific suggestion constant exists); update the
ThrowTypeError invocations to concatenate AMethod with the exported constant(s)
so both runtime diagnostics come from the shared message unit.
units/Goccia.Builtins.Semver.pas (1)

473-473: Prefer a shared SError* constant for this argument error.

Line 473 still uses an ad hoc literal string. Consider switching to a centralized error-message constant to keep SemVer diagnostics fully uniform.

Based on learnings: Applies to **/*.pas : Use split constant units for runtime constants instead of hardcoded string literals: Goccia.Constants.PropertyNames for property names, Goccia.Constants.TypeNames for type names, Goccia.Constants.ErrorNames for error names, Goccia.Constants.ConstructorNames for constructor names, and Goccia.Constants.SymbolNames for symbol names.

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

In `@units/Goccia.Builtins.Semver.pas` at line 473, Replace the ad-hoc literal in
the ThrowTypeError call with a shared error-message constant from the central
constants unit: add Goccia.Constants.ErrorNames to the uses clause if missing
and change ThrowTypeError(AMethodName + ' requires a string argument',
SSuggestStringArgRequired) to use the shared constant (e.g.
ThrowTypeError(AMethodName + ' ' + SErrorStringArgRequired,
SSuggestStringArgRequired) or ThrowTypeError(AMethodName +
SErrorStringArgRequired, SSuggestStringArgRequired)) referencing the constant
name (e.g. SErrorStringArgRequired) from Goccia.Constants.ErrorNames so SemVer
diagnostics use the uniform error message.
units/Goccia.Values.ClassValue.pas (1)

1249-1249: Suggestion text appears mismatched for assignment errors.

Line 1249, Line 1279, and Line 1285 use SSuggestCannotDeleteNonConfigurable for assignment failures. That hint reads delete-oriented and can confuse users during set/assign errors. Consider switching to an assignment-focused suggestion constant (or adding one).

Also applies to: 1279-1279, 1285-1285

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

In `@units/Goccia.Values.ClassValue.pas` at line 1249, The error message uses a
delete-oriented suggestion constant (SSuggestCannotDeleteNonConfigurable) when
throwing assignment errors in ThrowTypeError calls using
Format(SErrorCannotAssignReadOnly, [AName]); update those three call sites (the
ThrowTypeError invocations at lines referencing SErrorCannotAssignReadOnly) to
use an assignment-focused suggestion constant instead—either replace
SSuggestCannotDeleteNonConfigurable with a new constant like
SSuggestCannotAssignToReadOnly (and add that resource/string constant) or with
an existing assignment-oriented suggestion constant; ensure all three
occurrences are updated so assignment failures show the correct suggestion text.
units/Goccia.Values.TemporalPlainDateTime.pas (1)

154-159: Replace hardcoded Temporal field names with PROP_* constants in these touched paths.

CoercePlainDateTime and DateTimeWith still use string literals for Temporal fields ('year', 'month', 'day', etc.). Please switch these to Goccia.Constants.PropertyNames constants to keep Temporal property access consistent.

♻️ Proposed refactor
+  Goccia.Constants.PropertyNames,

-    VYear := GetRequiredField('year');
-    VMonth := GetRequiredField('month');
-    VDay := GetRequiredField('day');
-    VHour := GetOptionalField('hour', 0);
-    VMinute := GetOptionalField('minute', 0);
-    VSecond := GetOptionalField('second', 0);
+    VYear := GetRequiredField(PROP_YEAR);
+    VMonth := GetRequiredField(PROP_MONTH);
+    VDay := GetRequiredField(PROP_DAY);
+    VHour := GetOptionalField(PROP_HOUR, 0);
+    VMinute := GetOptionalField(PROP_MINUTE, 0);
+    VSecond := GetOptionalField(PROP_SECOND, 0);

-    GetFieldOr('year', D.FYear), GetFieldOr('month', D.FMonth), GetFieldOr('day', D.FDay),
-    GetFieldOr('hour', D.FHour), GetFieldOr('minute', D.FMinute), GetFieldOr('second', D.FSecond),
-    GetFieldOr('millisecond', D.FMillisecond), GetFieldOr('microsecond', D.FMicrosecond),
-    GetFieldOr('nanosecond', D.FNanosecond));
+    GetFieldOr(PROP_YEAR, D.FYear), GetFieldOr(PROP_MONTH, D.FMonth), GetFieldOr(PROP_DAY, D.FDay),
+    GetFieldOr(PROP_HOUR, D.FHour), GetFieldOr(PROP_MINUTE, D.FMinute), GetFieldOr(PROP_SECOND, D.FSecond),
+    GetFieldOr(PROP_MILLISECOND, D.FMillisecond), GetFieldOr(PROP_MICROSECOND, D.FMicrosecond),
+    GetFieldOr(PROP_NANOSECOND, D.FNanosecond));

Based on learnings: all Temporal value units must use Goccia.Constants.PropertyNames constants (e.g., PROP_YEAR, PROP_MONTH, PROP_DAY, time-unit PROP_*) instead of hardcoded literals.

Also applies to: 413-416

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

In `@units/Goccia.Values.TemporalPlainDateTime.pas` around lines 154 - 159,
Replace the hardcoded Temporal property name strings in CoercePlainDateTime and
DateTimeWith with the constants from Goccia.Constants.PropertyNames (e.g., use
PROP_YEAR, PROP_MONTH, PROP_DAY, PROP_HOUR, PROP_MINUTE, PROP_SECOND) wherever
GetRequiredField/GetOptionalField are called; update the same replacements in
the other affected block referenced (the later DateTimeWith/related code around
the second occurrence) so all temporal field lookups use the PropertyNames
constants instead of string literals.
units/Goccia.Values.TemporalPlainMonthDay.pas (1)

65-68: Finish migrating the remaining PlainMonthDay error literals.

These branches still hardcode message text, and the object-coercion errors also hardcode Temporal property names (day, monthCode, month). That leaves this unit out of sync with the shared diagnostics work and the Temporal property-name convention. Please format these through the shared SError* messages and PROP_DAY / PROP_MONTH_CODE / PROP_MONTH constants instead.

Based on learnings, all Temporal value unit implementations must use Goccia.Constants.PropertyNames::{PROP_YEAR, PROP_MONTH, PROP_MONTH_CODE, PROP_DAY, PROP_CALENDAR_ID} when constructing related error messages.

Also applies to: 101-127, 132-135

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

In `@units/Goccia.Values.TemporalPlainMonthDay.pas` around lines 65 - 68, The
branch in AsPlainMonthDay currently throws a hardcoded type error message;
replace that literal with the shared error message helpers (use the appropriate
SError* function/constant) and construct the object-coercion/error text using
the PropertyNames constants PROP_DAY, PROP_MONTH_CODE and PROP_MONTH from
Goccia.Constants.PropertyNames rather than hardcoded strings; update all similar
literal usages in this unit (including the other branches around the
PlainMonthDay coercions and the regions noted) to use the shared SError*
messages and the PROP_* constants when composing the error messages so the
Temporal property-name convention is respected.
units/Goccia.Evaluator.pas (1)

629-652: Keep call/construct suggestions on the shared suggestion catalog.

These fallback branches still synthesize the suggestion/detail text inline, so the new suggestion-aware output can drift again across equivalent “not callable” / “not constructable” errors. Please move the suggestion prose behind shared SSuggest* constants and keep only the contextual interpolation here.

Also applies to: 2023-2033

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

In `@units/Goccia.Evaluator.pas` around lines 629 - 652, The branches that
currently embed suggestion/detail strings inline (the ThrowTypeError calls
handling non-function/non-callable cases in units/Goccia.Evaluator.pas) should
use the shared suggestion constants instead of synthesizing text here: replace
the inline detail Format('''%s'' is of type ''%s'' which does not have method
''%s''', ...) and Format('''%s'' is of type ''%s'' and cannot be called as a
function', ...) and the final SSuggestNotFunctionType usage consistency by
passing the appropriate SSuggest* constant (e.g., SSuggestNotFunctionType or the
shared "not constructable" suggestion constant) to ThrowTypeError alongside the
contextual Format(...) primary message (using SErrorMemberNotFunction,
SErrorNotFunction, SErrorValueNotFunction). Update the branches that reference
MemberExpr, ACallExpression.Callee and TGocciaIdentifierExpression to use those
shared SSuggest* constants (also apply same change to the other occurrences
mentioned: lines 2023-2033).
units/Goccia.Builtins.TestAssertions.pas (1)

544-546: Prefer IsCallable for the new function validators.

These branches hard-code TGocciaFunctionBase, which is tighter than the runtime’s callability contract and keeps spreading the non-idiomatic check through the test API surface.

Based on learnings: Applies to units/**/*.pas : All values inherit from TGocciaValue. Prefer VMT methods GetProperty(Name), SetProperty(Name, Value), IsPrimitive, and IsCallable over type-checking at call sites. Do not use is type checks for fundamental type-system properties.

Also applies to: 2470-2471, 2501-2502, 3152-3155, 3195-3198, 3399-3400

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

In `@units/Goccia.Builtins.TestAssertions.pas` around lines 544 - 546, The code
currently checks AArguments.GetElement(1) is TGocciaFunctionBase which
hard-codes the concrete type; replace this type-check with the runtime
callability check by calling IsCallable on the value returned by
AArguments.GetElement(1) and keep the same error path: if not
AArguments.GetElement(1).IsCallable then call
Goccia.Values.ErrorHelper.ThrowTypeError(Format(SErrorFunctionExpectsFunctionSecond,[GetFunctionName]),
SSuggestTestUsage). Update the analogous occurrences mentioned (lines near 2470,
2501, 3152, 3195, 3399) to use IsCallable instead of "is TGocciaFunctionBase".
units/Goccia.Builtins.GlobalPromise.pas (1)

615-617: Use a shared message constant for the iterable TypeError path

This branch still composes an inline message (... + ' is not iterable'). Prefer a centralized SError... message (with Format(...) if needed) to keep diagnostics fully aligned with the new message catalog.

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

In `@units/Goccia.Builtins.GlobalPromise.pas` around lines 615 - 617, Replace the
inline error message concatenation in the TypeError branch with a centralized
message constant from the message catalog (use the appropriate SError...
constant), e.g. call Goccia.Values.ErrorHelper.ThrowTypeError with
Format(SErrorNotIterable, [Iterable.ToStringLiteral.Value]) instead of
Iterable.ToStringLiteral.Value + ' is not iterable'; keep SSuggestNotIterable as
the suggestion parameter and leave Result := nil unchanged.
units/Goccia.Values.TypedArrayValue.pas (1)

1716-1717: Consider a TypedArray.from-specific suggestion constant

TypedArray.from currently reuses SSuggestTypedArraySetSource. If you want cleaner UX copy, a dedicated from suggestion constant would avoid cross-method wording.

Also applies to: 1779-1779

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

In `@units/Goccia.Values.TypedArrayValue.pas` around lines 1716 - 1717, The code
is reusing SSuggestTypedArraySetSource for TypedArray.from which leads to
non-ideal UX copy; add a new suggestion constant (e.g.,
SSuggestTypedArrayFromSource) in the resource/strings module and replace
SSuggestTypedArraySetSource with SSuggestTypedArrayFromSource in the
ThrowTypeError calls used by TypedArray.from (the call that uses
ThrowTypeError(SErrorTypedArrayFromRequiresArg, SSuggestTypedArraySetSource) and
the similar occurrence around line 1779). Ensure the new constant text matches
"from"-specific wording and update any unit tests or references to use the new
SSuggestTypedArrayFromSource identifier.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@build/ppas.sh`:
- Around line 1-72: This PR includes a generated FPC artifact build/ppas.sh (and
its Windows counterpart build/ppas.bat) that should not be committed; remove
build/ppas.sh from the commit (and ensure build/ppas.bat is also untracked if
present) and add an entry to .gitignore to exclude these transient files (e.g.,
ignore build/ppas.sh and build/ppas.bat) so they don’t reappear in future diffs;
then amend the commit or create a new commit that deletes the file(s) from the
repo while leaving the .gitignore change.

In `@units/Goccia.Builtins.GlobalArrayBuffer.pas`:
- Around line 110-116: The current length coercion extracts Num :=
AArgs.GetElement(0).ToNumberLiteral and then manually validates/truncates it
(using Trunc and Trunc checks) which bypasses the canonical ToIndex logic;
replace this manual path by calling the standard ConstructorToIndex helper to
compute Len (and handle NaN, Infinity, negative, fractional, and large values)
and use its result for ArrayBuffer sizing; keep the ThrowRangeError call only
for constructor-level range failures if ConstructorToIndex indicates an error,
and update references to Num/Llen accordingly so Len is derived from
ConstructorToIndex instead of Trunc(Num.Value).

In `@units/Goccia.Builtins.GlobalMap.pas`:
- Around line 54-55: Map.groupBy currently rejects any input that is not a
TGocciaArrayValue by calling ThrowTypeError with
SErrorMapGroupByRequiresIterable; update the validation to accept either a
TGocciaArrayValue or any object that implements the iterator protocol your
runtime uses (e.g., checks for the iterator interface/type or a
GetIterator/MoveNext/Current pattern) before throwing. Locate the check using
AArgs.GetElement(0) and TGocciaArrayValue and expand it to also detect the
iterable/iterator type (or alternatively narrow the error text to explicitly say
“array” by keeping the array-only check and changing
SErrorMapGroupByRequiresIterable/SSuggestNotIterable), then only call
ThrowTypeError if neither condition is true so valid iterables do not get
rejected.

In `@units/Goccia.Builtins.GlobalString.pas`:
- Around line 138-139: The error message always reports 'undefined' even when
RawValue is null; update the check around RawValue in the conversion to compute
the correct kind string and pass that into ThrowTypeError: inspect RawValue
using the existing type checks (TGocciaUndefinedLiteralValue vs
TGocciaNullLiteralValue) to set a local kind variable to 'undefined' or 'null'
and then call ThrowTypeError(Format(SErrorCannotConvertToObject, [kind]),
SSuggestCheckNullBeforeAccess) so the reported value matches the actual
RawValue; target the code that references RawValue,
TGocciaUndefinedLiteralValue, TGocciaNullLiteralValue, ThrowTypeError,
SErrorCannotConvertToObject and SSuggestCheckNullBeforeAccess.

In `@units/Goccia.Builtins.GlobalSymbol.pas`:
- Line 180: Replace the incorrect prototype-receiver suggestion used when
throwing the keyFor type error: in the ThrowTypeError call that currently passes
SErrorSymbolKeyForRequiresSymbol and SSuggestSymbolThisType, swap
SSuggestSymbolThisType for a Symbol.keyFor-specific suggestion constant (or add
one if missing) so the thrown error uses a keyFor-argument-type suggestion;
update the ThrowTypeError invocation (the site using ThrowTypeError,
SErrorSymbolKeyForRequiresSymbol) to pass the new/appropriate suggestion token
instead of SSuggestSymbolThisType.

In `@units/Goccia.Builtins.Math.pas`:
- Around line 1023-1025: In the non-number branch of Math.sumPrecise replace the
incorrect suggestion token: where ThrowTypeError is called with
SErrorMathSumPreciseNotNumber and SSuggestNotIterable, change
SSuggestNotIterable to the number-specific suggestion token (e.g.,
SSuggestNotNumber) so the call becomes
Goccia.Values.ErrorHelper.ThrowTypeError(SErrorMathSumPreciseNotNumber,
SSuggestNotNumber); this affects the check that casts AElement to
TGocciaNumberLiteralValue before assigning NumVal.

In `@units/Goccia.Builtins.TestAssertions.pas`:
- Around line 541-550: The test-suggestion text (SSuggestTestUsage) added where
ErrorHelper.ThrowTypeError is called is lost because the TGocciaThrowValue
reporting/formatting path doesn't include suggestion strings; update the
TGocciaThrowValue formatter (the class/method responsible for producing the
thrown value message used by runTests) so it detects and includes any suggestion
text provided (e.g., append or include a suggestion field when TGocciaThrowValue
is constructed or when its ToString/Format method runs), and ensure throws
originating from Goccia.Values.ErrorHelper.ThrowTypeError preserve and surface
that suggestion in runTests output.

In `@units/Goccia.Builtins.TOML.pas`:
- Line 87: The ThrowSyntaxError call currently passes SSuggestStringArgRequired
which is misleading for TOML parse failures; update the call at the
ThrowSyntaxError(E.Message, SSuggestStringArgRequired) site to use the
TOML-specific suggestion constant (e.g., SSuggestTOMLSyntaxError or the
project's existing TOML parse suggestion constant) so syntax/parse errors
produce the correct guidance; keep the ThrowSyntaxError and E.Message parameters
unchanged and only swap the suggestion constant.

In `@units/Goccia.Builtins.YAML.pas`:
- Line 90: The parser is raising syntax errors with the wrong suggestion
constant (SSuggestStringArgRequired) in the ThrowSyntaxError calls at the YAML
parser locations (calls to ThrowSyntaxError around lines where the exception is
raised); update those ThrowSyntaxError calls in units/Goccia.Builtins.YAML.pas
(the two occurrences referencing SSuggestStringArgRequired) to omit the
misleading suggestion or pass a neutral/empty suggestion value instead (i.e.,
remove SSuggestStringArgRequired from the ThrowSyntaxError call or replace it
with a neutral suggestion constant such as an empty string or SSuggestNone if
available) so the error message no longer directs users incorrectly.

In `@units/Goccia.Values.ArrayValue.pas`:
- Around line 408-410: The TGocciaArrayValue.ThrowError method currently raises
TGocciaError directly; change it to delegate to the project error helpers in
Goccia.Values.ErrorHelper (e.g., call ThrowError, or
ThrowTypeError/ThrowRangeError as appropriate) instead of using
TGocciaError.Create so all runtime array validation errors flow through the
standard helper pipeline; update the method to format the message (using
Format(AMessage, AArgs)) and pass that text plus the ASuggestion into the
correct helper routine and ensure the ErrorHelper unit is referenced so no
direct TGocciaError construction remains.

In `@units/Goccia.Values.FFIPointer.pas`:
- Line 147: Replace hard-coded property name string literals used in formatted
error messages with the corresponding property-name constants: change the
'isNull' argument in the Format call passed to ThrowTypeError to use
PROP_IS_NULL, and likewise replace any 'address' literal (e.g., the similar
Format/ThrowTypeError at the other location) with PROP_FFI_ADDRESS; import or
reference the constant container (Goccia.Constants.PropertyNames) if not already
available so the Format calls in the ThrowTypeError/Format expressions use
PROP_IS_NULL and PROP_FFI_ADDRESS instead of literal strings.

In `@units/Goccia.Values.TemporalPlainDate.pas`:
- Around line 130-136: Replace the hardcoded Temporal property names by using
the constants from Goccia.Constants.PropertyNames: change string literals passed
to Obj.GetProperty ('year','month','day') to the corresponding constants
(PROP_YEAR, PROP_MONTH, PROP_DAY) and update the ThrowTypeError messages that
currently embed "year, month, day" to build the message using those same
constants (keeping the existing use of ThrowTypeError, SSuggestTemporalFromArg,
Obj.GetProperty and TGocciaUndefinedLiteralValue). Ensure all references are
consistent so property access and the error text rely on the PropertyNames
constants rather than literal strings.

In `@units/Goccia.Values.TextEncoderValue.pas`:
- Line 239: The ThrowTypeError call currently uses SSuggestTextEncoderThisType
while validating encodeInto argument/destination shape; update the three
ThrowTypeError calls (those invoking ThrowTypeError with
SErrorTextEncoderEncodeIntoArgs and SSuggestTextEncoderThisType) to use a more
appropriate argument/destination-focused suggestion constant (e.g.,
SSuggestTextEncoderEncodeIntoArgs or SSuggestTextEncoderDestinationType) so the
thrown error points users at the invalid argument/destination rather than
suggesting a problem with this; modify the ThrowTypeError invocations in the
encodeInto validation branches in Goccia.Values.TextEncoderValue.pas to
reference the new suggestion symbol and ensure the error message remains
consistent with SErrorTextEncoderEncodeIntoArgs.

In `@units/Goccia.VM.pas`:
- Line 2413: The ThrowTypeError calls currently pass the wrong suggestion
constant (SSuggestNotFunctionType) for module/import failures; update both
ThrowTypeError(SErr... SErrorModuleNotAvailableInVM, SSuggestNotFunctionType)
occurrences to use the module-availability suggestion constant instead (e.g.,
SSuggestModuleNotAvailable) so the suggestion matches
SErrorModuleNotAvailableInVM.
- Around line 3436-3437: The current ThrowTypeError call always uses
SSuggestCheckNullBeforeAccess which is inappropriate for non-null primitives;
change this to select the suggestion based on the value kind(s) before throwing.
Locate the ThrowTypeError usage (symbols: ThrowTypeError, Format,
SErrorCannotUseInOperator, AKey, AObject, SSuggestCheckNullBeforeAccess) and
replace the fixed suggestion with a small branch that inspects AObject (and/or
AKey) kind (e.g., IsBoolean/IsNumber/IsString/IsObject/IsNull) and picks an
appropriate suggestion string for primitives versus null/object access (or a
generic "ensure value is an object or primitive" message) before calling
ThrowTypeError. Ensure the formatted error text still uses
SErrorCannotUseInOperator and only the suggestion token differs.
- Around line 345-346: The suggestion constant used with
ThrowTypeError(Format(SErrorCannotAssignReadOnly, [AName]),
SSuggestCannotDeleteNonConfigurable) is incorrect for a read-only assignment
error; replace SSuggestCannotDeleteNonConfigurable with a
read-only/assignment-specific suggestion constant (e.g.,
SSuggestCannotAssignReadOnly or SSuggestCannotWriteToReadOnly) so the second
argument matches SErrorCannotAssignReadOnly, and if such a constant doesn't
exist, add a properly named suggestion constant and use it in the ThrowTypeError
call.

---

Outside diff comments:
In `@units/Goccia.Error.Detail.pas`:
- Around line 120-133: The fallback branch that builds Result for
non-source-context errors drops ASuggestion, so when TGocciaObjectValue fallback
or the final else set Result (using StackValue or AThrown.ToStringLiteral.Value)
they must also append the suggestion exactly as the source-context path does;
update the fallback assignments of Result to include ASuggestion when provided
(e.g., check ASuggestion<>'' and concatenate it with the existing Result string)
so diagnostics remain consistent with the source-context branch.

In `@units/Goccia.Values.Iterator.Lazy.pas`:
- Around line 552-557: The code currently calls FSourceIterator.Close directly
before ThrowTypeError which can raise and mask the intended TypeError from
ThrowTypeError; update the two locations (around ResolveIterator /
FInnerIterator and the other block at 612-617) to use the local
exception-preservation helper (i.e., save the current exception, call
FSourceIterator.Close within a try/finally that restores the saved exception) so
that any exception from Close does not replace the
ThrowTypeError(SErrorIteratorFlatMapMustReturnIterable,
SSuggestIteratorFlatMapCallable) error; ensure the helper wraps the Close call
around FSourceIterator.Close while still allowing proper cleanup.

---

Nitpick comments:
In `@units/Goccia.Builtins.GlobalPromise.pas`:
- Around line 615-617: Replace the inline error message concatenation in the
TypeError branch with a centralized message constant from the message catalog
(use the appropriate SError... constant), e.g. call
Goccia.Values.ErrorHelper.ThrowTypeError with Format(SErrorNotIterable,
[Iterable.ToStringLiteral.Value]) instead of Iterable.ToStringLiteral.Value + '
is not iterable'; keep SSuggestNotIterable as the suggestion parameter and leave
Result := nil unchanged.

In `@units/Goccia.Builtins.Semver.pas`:
- Line 473: Replace the ad-hoc literal in the ThrowTypeError call with a shared
error-message constant from the central constants unit: add
Goccia.Constants.ErrorNames to the uses clause if missing and change
ThrowTypeError(AMethodName + ' requires a string argument',
SSuggestStringArgRequired) to use the shared constant (e.g.
ThrowTypeError(AMethodName + ' ' + SErrorStringArgRequired,
SSuggestStringArgRequired) or ThrowTypeError(AMethodName +
SErrorStringArgRequired, SSuggestStringArgRequired)) referencing the constant
name (e.g. SErrorStringArgRequired) from Goccia.Constants.ErrorNames so SemVer
diagnostics use the uniform error message.

In `@units/Goccia.Builtins.TestAssertions.pas`:
- Around line 544-546: The code currently checks AArguments.GetElement(1) is
TGocciaFunctionBase which hard-codes the concrete type; replace this type-check
with the runtime callability check by calling IsCallable on the value returned
by AArguments.GetElement(1) and keep the same error path: if not
AArguments.GetElement(1).IsCallable then call
Goccia.Values.ErrorHelper.ThrowTypeError(Format(SErrorFunctionExpectsFunctionSecond,[GetFunctionName]),
SSuggestTestUsage). Update the analogous occurrences mentioned (lines near 2470,
2501, 3152, 3195, 3399) to use IsCallable instead of "is TGocciaFunctionBase".

In `@units/Goccia.Evaluator.pas`:
- Around line 629-652: The branches that currently embed suggestion/detail
strings inline (the ThrowTypeError calls handling non-function/non-callable
cases in units/Goccia.Evaluator.pas) should use the shared suggestion constants
instead of synthesizing text here: replace the inline detail Format('''%s'' is
of type ''%s'' which does not have method ''%s''', ...) and Format('''%s'' is of
type ''%s'' and cannot be called as a function', ...) and the final
SSuggestNotFunctionType usage consistency by passing the appropriate SSuggest*
constant (e.g., SSuggestNotFunctionType or the shared "not constructable"
suggestion constant) to ThrowTypeError alongside the contextual Format(...)
primary message (using SErrorMemberNotFunction, SErrorNotFunction,
SErrorValueNotFunction). Update the branches that reference MemberExpr,
ACallExpression.Callee and TGocciaIdentifierExpression to use those shared
SSuggest* constants (also apply same change to the other occurrences mentioned:
lines 2023-2033).

In `@units/Goccia.Values.ClassValue.pas`:
- Line 1249: The error message uses a delete-oriented suggestion constant
(SSuggestCannotDeleteNonConfigurable) when throwing assignment errors in
ThrowTypeError calls using Format(SErrorCannotAssignReadOnly, [AName]); update
those three call sites (the ThrowTypeError invocations at lines referencing
SErrorCannotAssignReadOnly) to use an assignment-focused suggestion constant
instead—either replace SSuggestCannotDeleteNonConfigurable with a new constant
like SSuggestCannotAssignToReadOnly (and add that resource/string constant) or
with an existing assignment-oriented suggestion constant; ensure all three
occurrences are updated so assignment failures show the correct suggestion text.

In `@units/Goccia.Values.TemporalInstant.pas`:
- Line 64: Replace the two hardcoded Temporal.Instant TypeError messages that
call ThrowTypeError (the instance using AMethod + ' called on non-Instant',
SSuggestTemporalFromArg and the similar message at the other occurrence) with
shared string constants located in the centralized constants unit (e.g.,
Goccia.Constants.ErrorNames or a new SErrorTemporalInstant constant), then
update the ThrowTypeError calls to use those constants; locate the usages by
searching for ThrowTypeError and the concatenation pattern (AMethod + ' called
on non-Instant') and replace the literal strings with the new SError* constant
names so this unit aligns with the project's standardized error constants.

In `@units/Goccia.Values.TemporalPlainDateTime.pas`:
- Around line 154-159: Replace the hardcoded Temporal property name strings in
CoercePlainDateTime and DateTimeWith with the constants from
Goccia.Constants.PropertyNames (e.g., use PROP_YEAR, PROP_MONTH, PROP_DAY,
PROP_HOUR, PROP_MINUTE, PROP_SECOND) wherever GetRequiredField/GetOptionalField
are called; update the same replacements in the other affected block referenced
(the later DateTimeWith/related code around the second occurrence) so all
temporal field lookups use the PropertyNames constants instead of string
literals.

In `@units/Goccia.Values.TemporalPlainMonthDay.pas`:
- Around line 65-68: The branch in AsPlainMonthDay currently throws a hardcoded
type error message; replace that literal with the shared error message helpers
(use the appropriate SError* function/constant) and construct the
object-coercion/error text using the PropertyNames constants PROP_DAY,
PROP_MONTH_CODE and PROP_MONTH from Goccia.Constants.PropertyNames rather than
hardcoded strings; update all similar literal usages in this unit (including the
other branches around the PlainMonthDay coercions and the regions noted) to use
the shared SError* messages and the PROP_* constants when composing the error
messages so the Temporal property-name convention is respected.

In `@units/Goccia.Values.TemporalPlainTime.pas`:
- Around line 77-78: Two remaining hardcoded PlainTime diagnostic messages in
this unit should be replaced with centralized constants from
Goccia.Error.Messages: replace the literal string used in the ThrowTypeError
call that builds AMethod + ' called on non-PlainTime' (the call immediately
before the TGocciaTemporalPlainTimeValue(AValue) cast) and the other literal
used around lines 118-119 with appropriate constants from Goccia.Error.Messages
(e.g., PlainTimeCalledOnNonPlainTime and the corresponding suggestion constant
instead of SSuggestTemporalFromArg if a specific suggestion constant exists);
update the ThrowTypeError invocations to concatenate AMethod with the exported
constant(s) so both runtime diagnostics come from the shared message unit.

In `@units/Goccia.Values.TemporalPlainYearMonth.pas`:
- Around line 103-104: Replace the ad-hoc PlainYearMonth error message strings
used in ThrowTypeError and other places with shared constants from the
Goccia.Error.Messages unit: define appropriate constants (e.g.,
ErrNotPlainYearMonth, ErrPlainYearMonthSuggestFromArg, etc.) in
Goccia.Error.Messages and import that unit into
units/Goccia.Values.TemporalPlainYearMonth.pas, then update the calls
(ThrowTypeError(AMethod + ' called on non-PlainYearMonth',
SSuggestTemporalFromArg), and the messages at the locations referenced around
the TGocciaTemporalPlainYearMonthValue checks at lines ~161–165 and ~171–172) to
use those constants instead of string literals so all runtime error text is
centralized.

In `@units/Goccia.Values.TemporalZonedDateTime.pas`:
- Around line 115-116: Replace the ad-hoc literal error text used when
validating ZonedDateTime values by using the centralized error/messages
constants and helper: instead of calling ThrowTypeError(AMethod + ' called on
non-ZonedDateTime', SSuggestTemporalFromArg) and returning
TGocciaTemporalZonedDateTimeValue(AValue) with hardcoded strings, assemble the
error text using the appropriate constant from Goccia.Error.Messages (or the
split runtime constants in Goccia.Constants.*) and pass that into ThrowTypeError
(or the shared error-construction helper) so all messages in
TGocciaTemporalZonedDateTimeValue validation use the centralized catalog; update
the other occurrences mentioned (the other checks around
TGocciaTemporalZonedDateTimeValue) to follow the same pattern.

In `@units/Goccia.Values.TypedArrayValue.pas`:
- Around line 1716-1717: The code is reusing SSuggestTypedArraySetSource for
TypedArray.from which leads to non-ideal UX copy; add a new suggestion constant
(e.g., SSuggestTypedArrayFromSource) in the resource/strings module and replace
SSuggestTypedArraySetSource with SSuggestTypedArrayFromSource in the
ThrowTypeError calls used by TypedArray.from (the call that uses
ThrowTypeError(SErrorTypedArrayFromRequiresArg, SSuggestTypedArraySetSource) and
the similar occurrence around line 1779). Ensure the new constant text matches
"from"-specific wording and update any unit tests or references to use the new
SSuggestTypedArrayFromSource identifier.
🪄 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: 66cf807e-b410-42da-8ee6-a72c2c06030c

📥 Commits

Reviewing files that changed from the base of the PR and between 8263d7c and 2a3f19b.

📒 Files selected for processing (81)
  • BenchmarkRunner.dpr
  • REPL.dpr
  • ScriptLoader.dpr
  • TestRunner.dpr
  • build/ppas.sh
  • units/Goccia.Application.pas
  • units/Goccia.Builtins.DisposableStack.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.GlobalArrayBuffer.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Builtins.Temporal.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Error.Suggestions.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Scope.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.URI.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.Iterator.Zip.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.SymbolValue.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Values.TemporalPlainDate.pas
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas

Comment thread build/ppas.sh Outdated
Comment thread units/Goccia.Builtins.GlobalArrayBuffer.pas
Comment thread units/Goccia.Builtins.GlobalMap.pas
Comment thread units/Goccia.Builtins.GlobalString.pas Outdated
Comment thread units/Goccia.Builtins.GlobalSymbol.pas Outdated
Comment thread units/Goccia.Values.TemporalPlainDate.pas
Comment thread units/Goccia.Values.TextEncoderValue.pas Outdated
Comment thread units/Goccia.VM.pas Outdated
Comment thread units/Goccia.VM.pas Outdated
Comment thread units/Goccia.VM.pas
@frostney frostney force-pushed the t3code/align-error-suggestions branch from 2a3f19b to 7532357 Compare April 15, 2026 15:40
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

Caution

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

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

431-446: ⚠️ Potential issue | 🟠 Major

Handle unsupported withPlainTime argument kinds explicitly.

At Line 431, non-undefined values that are neither TGocciaTemporalPlainTimeValue nor string currently fall through and return midnight (Line 448) instead of throwing. Please add an explicit else error branch so invalid inputs don’t silently coerce.

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

In `@units/Goccia.Values.TemporalPlainDateTime.pas` around lines 431 - 446, The
current Arg handling in the withPlainTime path lets non-undefined,
non-TGocciaTemporalPlainTimeValue and non-TGocciaStringLiteralValue fall through
to midnight; update the conditional block that checks Arg (the Assigned(Arg) ...
branch) to add a final else that calls ThrowTypeError with an appropriate
message for invalid withPlainTime argument kinds (use the same ThrowTypeError
mechanism as used for time parsing errors), e.g. indicate an unsupported
argument type for withPlainTime; reference TGocciaTemporalPlainTimeValue,
TGocciaStringLiteralValue, TGocciaUndefinedLiteralValue, TryParseISOTime,
ThrowTypeError, SErrorInvalidTimeString and SSuggestTemporalISOFormat so the fix
is placed in the same branch that currently handles those cases.
♻️ Duplicate comments (1)
units/Goccia.Values.TemporalPlainDate.pas (1)

67-68: ⚠️ Potential issue | 🟡 Minor

Replace hardcoded Temporal property-name literals with shared constants.

CoercePlainDate still hardcodes year/month/day in both property access and error text, which can drift from the shared property-name source.

♻️ Proposed fix
 uses
   SysUtils,

+  Goccia.Constants.PropertyNames,
   Goccia.Error.Messages,
   Goccia.Error.Suggestions,
@@
-    V := Obj.GetProperty('year');
+    V := Obj.GetProperty(PROP_YEAR);
     if (V = nil) or (V is TGocciaUndefinedLiteralValue) then
-      ThrowTypeError(AMethod + ' requires year, month, day properties', SSuggestTemporalFromArg);
-    VMonth := Obj.GetProperty('month');
+      ThrowTypeError(
+        Format('%s requires %s, %s, %s properties', [AMethod, PROP_YEAR, PROP_MONTH, PROP_DAY]),
+        SSuggestTemporalFromArg);
+    VMonth := Obj.GetProperty(PROP_MONTH);
     if (VMonth = nil) or (VMonth is TGocciaUndefinedLiteralValue) then
-      ThrowTypeError(AMethod + ' requires year, month, day properties', SSuggestTemporalFromArg);
-    VDay := Obj.GetProperty('day');
+      ThrowTypeError(
+        Format('%s requires %s, %s, %s properties', [AMethod, PROP_YEAR, PROP_MONTH, PROP_DAY]),
+        SSuggestTemporalFromArg);
+    VDay := Obj.GetProperty(PROP_DAY);
     if (VDay = nil) or (VDay is TGocciaUndefinedLiteralValue) then
-      ThrowTypeError(AMethod + ' requires year, month, day properties', SSuggestTemporalFromArg);
+      ThrowTypeError(
+        Format('%s requires %s, %s, %s properties', [AMethod, PROP_YEAR, PROP_MONTH, PROP_DAY]),
+        SSuggestTemporalFromArg);

Based on learnings: Temporal units must use Goccia.Constants.PropertyNames constants (PROP_YEAR, PROP_MONTH, PROP_DAY) for Temporal property access and related error-message references.

Also applies to: 128-136

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

In `@units/Goccia.Values.TemporalPlainDate.pas` around lines 67 - 68,
CoercePlainDate currently hardcodes the Temporal property-name literals "year",
"month", and "day" in property access and error messages; update it to reference
the shared constants PROP_YEAR, PROP_MONTH, and PROP_DAY from
Goccia.Constants.PropertyNames instead. Replace any direct string keys when
reading properties and any occurrences inside
Goccia.Error.Messages/Goccia.Error.Suggestions text construction with those
constants (e.g., in CoercePlainDate and the related validation/formatting code
around the other Temporal plain-date helpers), ensuring you import or qualify
Goccia.Constants.PropertyNames and use the constants in both lookups and
error-message formatting so the names remain in sync.
🧹 Nitpick comments (1)
units/Goccia.Values.TemporalPlainMonthDay.pas (1)

101-127: Keep Temporal property names sourced from PROP_* in the new error text.

These messages inline day, monthCode, and month even though this unit already imports PROP_DAY, PROP_MONTH_CODE, and PROP_MONTH. Using the constants in the message construction keeps the diagnostics aligned with the rest of the Temporal units and avoids string drift.

♻️ Suggested cleanup
-      ThrowTypeError(AMethod + ' requires day property', SSuggestTemporalFromArg);
+      ThrowTypeError(
+        Format('%s requires %s property', [AMethod, PROP_DAY]),
+        SSuggestTemporalFromArg);
...
-        ThrowTypeError(AMethod + ' requires monthCode or month property', SSuggestTemporalFromArg);
+        ThrowTypeError(
+          Format('%s requires %s or %s property',
+            [AMethod, PROP_MONTH_CODE, PROP_MONTH]),
+          SSuggestTemporalFromArg);

Based on learnings, Temporal units should use Goccia.Constants.PropertyNames::{PROP_YEAR, PROP_MONTH, PROP_MONTH_CODE, PROP_DAY, PROP_CALENDAR_ID} when constructing related error messages.

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

In `@units/Goccia.Values.TemporalPlainMonthDay.pas` around lines 101 - 127,
Replace the inline property names in the thrown error texts with the existing
PROP_* constants: change the ThrowTypeError that currently concatenates '
requires day property' to use PROP_DAY (e.g. Format or concatenation with
PROP_DAY) where VDay is validated; change the message in the else branch that
says ' requires monthCode or month property' to reference PROP_MONTH_CODE and
PROP_MONTH instead of literal names; and ensure any other error messages related
to monthCode/month consistency (the ThrowTypeError/ThrowRangeError calls inside
the VMonthCode handling that reference monthCode/mismatch) similarly use
PROP_MONTH_CODE or PROP_MONTH constants; locate these usages around symbols
VDay, VMonthCode, V, MonthCodeStr, MonthPart and update message construction to
use the PROP_* constants.
🤖 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.Builtins.JSONL.pas`:
- Around line 150-153: Replace misleading suggestion constants in the JSONL
argument-type error paths: where ThrowTypeError is called with
SErrorJSONLParseArgType and SSuggestStringArgRequired, remove the string-only
hint (pass an empty suggestion or a neutral hint) so the error doesn’t
incorrectly force “string” only; likewise, in the EGocciaJSONLInvalidInputError
handler replace SSuggestJSONFormat with an empty suggestion (or neutral hint) so
non-typed-array invalid inputs aren’t given an incorrect JSON format suggestion;
update both occurrences (the shown block and the similar block at the later
occurrence) to use the neutral/empty suggestion instead of
SSuggestStringArgRequired/SSuggestJSONFormat.

In `@units/Goccia.Scope.pas`:
- Around line 240-243: Replace direct instantiation and raising of
TGocciaSyntaxError/TGocciaTypeError/TGocciaReferenceError in Goccia.Scope.pas
with the centralized helpers from Goccia.Values.ErrorHelper: use
ThrowSyntaxError, ThrowTypeError or ThrowReferenceError respectively. For each
site (e.g., the block that currently does
TGocciaSyntaxError.Create(Format(SErrorIdentifierAlreadyDeclared, [AName]),
ALine, AColumn, '', nil) followed by Error.Suggestion :=
SSuggestAlreadyDeclared; raise Error) call ThrowSyntaxError with the same
formatted message, ALine and AColumn and set the suggestion through the helper
parameters or by passing the suggestion text as the helper supports; do the
analogous replacement for the other ranges (around lines mentioned: 295-305,
322-325, 335-338, 344-347) replacing TGocciaTypeError and TGocciaReferenceError
usages with ThrowTypeError and ThrowReferenceError while preserving message,
line, column and suggestion values. Ensure you include the appropriate uses
clause for Goccia.Values.ErrorHelper if not already present.

In `@units/Goccia.Values.TemporalPlainMonthDay.pas`:
- Around line 65-68: The current generic suggestion SSuggestTemporalFromArg is
too broad; update AsPlainMonthDay to call ThrowTypeError with a narrower
suggestion when the receiver is the wrong type (e.g., "AsPlainMonthDay called on
non-PlainMonthDay — pass a PlainMonthDay Temporal object") instead of
SSuggestTemporalFromArg, and update the MonthDayToPlainDate-related checks to
throw a distinct error message when the input lacks a year property (e.g.,
"MonthDayToPlainDate requires an object with a numeric 'year' property") so
users are not steered toward inputs those branches will still reject; also
replace similar SSuggestTemporalFromArg usages in the other related helper
checks (the other occurrences flagged) with these specific wrong-receiver vs
missing-year messages.

---

Outside diff comments:
In `@units/Goccia.Values.TemporalPlainDateTime.pas`:
- Around line 431-446: The current Arg handling in the withPlainTime path lets
non-undefined, non-TGocciaTemporalPlainTimeValue and
non-TGocciaStringLiteralValue fall through to midnight; update the conditional
block that checks Arg (the Assigned(Arg) ... branch) to add a final else that
calls ThrowTypeError with an appropriate message for invalid withPlainTime
argument kinds (use the same ThrowTypeError mechanism as used for time parsing
errors), e.g. indicate an unsupported argument type for withPlainTime; reference
TGocciaTemporalPlainTimeValue, TGocciaStringLiteralValue,
TGocciaUndefinedLiteralValue, TryParseISOTime, ThrowTypeError,
SErrorInvalidTimeString and SSuggestTemporalISOFormat so the fix is placed in
the same branch that currently handles those cases.

---

Duplicate comments:
In `@units/Goccia.Values.TemporalPlainDate.pas`:
- Around line 67-68: CoercePlainDate currently hardcodes the Temporal
property-name literals "year", "month", and "day" in property access and error
messages; update it to reference the shared constants PROP_YEAR, PROP_MONTH, and
PROP_DAY from Goccia.Constants.PropertyNames instead. Replace any direct string
keys when reading properties and any occurrences inside
Goccia.Error.Messages/Goccia.Error.Suggestions text construction with those
constants (e.g., in CoercePlainDate and the related validation/formatting code
around the other Temporal plain-date helpers), ensuring you import or qualify
Goccia.Constants.PropertyNames and use the constants in both lookups and
error-message formatting so the names remain in sync.

---

Nitpick comments:
In `@units/Goccia.Values.TemporalPlainMonthDay.pas`:
- Around line 101-127: Replace the inline property names in the thrown error
texts with the existing PROP_* constants: change the ThrowTypeError that
currently concatenates ' requires day property' to use PROP_DAY (e.g. Format or
concatenation with PROP_DAY) where VDay is validated; change the message in the
else branch that says ' requires monthCode or month property' to reference
PROP_MONTH_CODE and PROP_MONTH instead of literal names; and ensure any other
error messages related to monthCode/month consistency (the
ThrowTypeError/ThrowRangeError calls inside the VMonthCode handling that
reference monthCode/mismatch) similarly use PROP_MONTH_CODE or PROP_MONTH
constants; locate these usages around symbols VDay, VMonthCode, V, MonthCodeStr,
MonthPart and update message construction to use the PROP_* constants.
🪄 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: 5651445e-8522-47f1-ac76-e6d5232e1411

📥 Commits

Reviewing files that changed from the base of the PR and between 2a3f19b and 7532357.

📒 Files selected for processing (81)
  • .gitignore
  • BenchmarkRunner.dpr
  • REPL.dpr
  • ScriptLoader.dpr
  • TestRunner.dpr
  • units/Goccia.Application.pas
  • units/Goccia.Builtins.DisposableStack.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.GlobalArrayBuffer.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Builtins.Temporal.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Error.Suggestions.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Scope.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.URI.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.Iterator.Zip.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.SymbolValue.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Values.TemporalPlainDate.pas
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas
✅ Files skipped from review due to trivial changes (16)
  • .gitignore
  • units/Goccia.Builtins.GlobalArrayBuffer.pas
  • ScriptLoader.dpr
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Values.SymbolValue.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Error.Messages.pas
🚧 Files skipped from review as they are similar to previous changes (46)
  • units/Goccia.Application.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Values.MapValue.pas
  • TestRunner.dpr
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Builtins.Temporal.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Error.Suggestions.pas

Comment thread units/Goccia.Builtins.JSONL.pas Outdated
Comment thread units/Goccia.Evaluator.pas
Comment thread units/Goccia.Scope.pas
Comment on lines +240 to 243
Error := TGocciaSyntaxError.Create(Format(SErrorIdentifierAlreadyDeclared, [AName]), ALine, AColumn, '', nil);
Error.Suggestion := SSuggestAlreadyDeclared;
raise Error;
end;
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 15, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use Goccia.Values.ErrorHelper for these runtime error paths.

These changed scope-runtime throws are constructed directly via TGocciaSyntaxError/TGocciaTypeError/TGocciaReferenceError. Please route them through ThrowSyntaxError / ThrowTypeError / ThrowReferenceError helpers to keep runtime error construction consistent with repository policy.

As per coding guidelines: "All runtime errors must use error helper functions (ThrowTypeError, ThrowRangeError, ThrowReferenceError, ThrowSyntaxError, ThrowError, ThrowDataCloneError) from Goccia.Values.ErrorHelper.pas. Never create TGocciaError directly."

Also applies to: 295-305, 322-325, 335-338, 344-347

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

In `@units/Goccia.Scope.pas` around lines 240 - 243, Replace direct instantiation
and raising of TGocciaSyntaxError/TGocciaTypeError/TGocciaReferenceError in
Goccia.Scope.pas with the centralized helpers from Goccia.Values.ErrorHelper:
use ThrowSyntaxError, ThrowTypeError or ThrowReferenceError respectively. For
each site (e.g., the block that currently does
TGocciaSyntaxError.Create(Format(SErrorIdentifierAlreadyDeclared, [AName]),
ALine, AColumn, '', nil) followed by Error.Suggestion :=
SSuggestAlreadyDeclared; raise Error) call ThrowSyntaxError with the same
formatted message, ALine and AColumn and set the suggestion through the helper
parameters or by passing the suggestion text as the helper supports; do the
analogous replacement for the other ranges (around lines mentioned: 295-305,
322-325, 335-338, 344-347) replacing TGocciaTypeError and TGocciaReferenceError
usages with ThrowTypeError and ThrowReferenceError while preserving message,
line, column and suggestion values. Ensure you include the appropriate uses
clause for Goccia.Values.ErrorHelper if not already present.

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 actioned — scope errors use TGocciaError subclasses (TGocciaSyntaxError, TGocciaReferenceError, TGocciaTypeError) which carry source position (line, column, filename, source lines) directly in the constructor. The ErrorHelper.Throw* functions create TGocciaThrowValue instead, which relies on call stack trace extraction for location. These are architecturally different: TGocciaError is for errors with known source positions (parser, scope), TGocciaThrowValue is for runtime JS errors. Switching would lose the precise source position that makes scope errors point to the exact variable declaration.

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 — that's a meaningful architectural distinction. TGocciaError subclasses in Goccia.Scope.pas carry precise source position in their constructors, whereas ErrorHelper.Throw* produces TGocciaThrowValue whose location comes from call-stack extraction. Conflating them would lose the exact variable/declaration position in scope errors. I'll update my understanding accordingly.


✏️ Learnings added
Learnt from: frostney
Repo: frostney/GocciaScript PR: 308
File: units/Goccia.Scope.pas:240-243
Timestamp: 2026-04-15T20:56:24.170Z
Learning: In frostney/GocciaScript (units/Goccia.Scope.pas and the parser layer), TGocciaSyntaxError, TGocciaReferenceError, and TGocciaTypeError are intentionally constructed directly (not via ErrorHelper.Throw* helpers) because they carry precise source position (line, column, filename, source lines) in their constructors. ErrorHelper.Throw* functions produce TGocciaThrowValue, which relies on call-stack trace extraction for location and is intended for runtime JS errors. Do NOT flag direct TGocciaError subclass construction in Goccia.Scope.pas (DefineLexicalBinding, AssignLexicalBinding, GetLexicalBinding) or in parser code as violating the "use ErrorHelper, never construct TGocciaError directly" rule — these are architecturally distinct paths where the source position is known at throw time.

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: frostney
Repo: frostney/GocciaScript PR: 200
File: units/Goccia.Compiler.Statements.pas:569-570
Timestamp: 2026-04-07T17:12:43.685Z
Learning: In frostney/GocciaScript (units/Goccia.Compiler.Statements.pas, units/Goccia.Engine.Backend.pas, units/Goccia.Scope.Redeclaration.pas), the `EmitGlobalDefine` / `OP_DEFINE_GLOBAL_CONST` calls in `CompileVariableDeclaration`, `CompileClassDeclaration`, `CompileDestructuringDeclaration`, and `CompileEnumDeclaration` intentionally execute *after* the RHS/initializer has been evaluated. Redeclaration protection is handled up-front by `CheckTopLevelRedeclarations` (an AST-level pre-flight walk) called from `TGocciaBytecodeBackend.CompileToModule` (when `GlobalBackedTopLevel` is true) and from `TGocciaEngine.Execute` (after parsing, before execution). This catches all top-level `let`/`const`/class/enum redeclarations before any RHS side effects can run, matching V8 early-error behaviour. Do NOT re-flag the post-evaluation placement of `EmitGlobalDefine` as a side-effect ordering bug.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 308
File: units/Goccia.Values.ArrayValue.pas:408-410
Timestamp: 2026-04-15T15:41:26.217Z
Learning: In frostney/GocciaScript (units/Goccia.Values.ArrayValue.pas), `TGocciaArrayValue.ThrowError` is a pre-existing internal engine assertion helper that raises `TGocciaError` (a Pascal exception for engine-internal validation). It is architecturally distinct from `Goccia.Values.ErrorHelper` helpers (`ThrowTypeError`, `ThrowRangeError`, etc.) which produce `TGocciaThrowValue` — a JS-visible error catchable from JavaScript `try…catch`. Direct `TGocciaError.Create` usage inside `TGocciaArrayValue.ThrowError` (including the overload with `ASuggestion`) is intentional and must NOT be flagged as violating the "use ErrorHelper, never construct TGocciaError directly" rule; the array method guards are engine assertions, not user-facing JS errors. Do not recommend delegating `TGocciaArrayValue.ThrowError` to `ErrorHelper` helpers.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 302
File: docs/interpreter.md:42-42
Timestamp: 2026-04-14T13:26:33.825Z
Learning: In frostney/GocciaScript (units/Goccia.Scope.pas, units/Goccia.Evaluator.pas): `OnError: TGocciaThrowErrorCallback` exists on BOTH `TGocciaScope` (property backed by `FOnError` at line 70, copied from parent via `FOnError := AParent.FOnError` in the constructor at line 153) AND `TGocciaEvaluationContext` (record field). `TGocciaScope.CreateChild` propagates `OnError` to child scopes because it calls `TGocciaScope.Create(Self, ...)` and the constructor copies `FOnError` from the parent. The evaluator additionally syncs `AContext.OnError` to `AContext.Scope.OnError` as a bridge. Do NOT flag the doc claim that "OnError is stored on TGocciaScope and propagated to child scopes" as incorrect — it is accurate.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 308
File: units/Goccia.VM.pas:0-0
Timestamp: 2026-04-15T15:41:55.194Z
Learning: In this repo’s Pascal code, when handling “assigning to a read-only” errors, use the exact suggestion type `SSuggestReadOnlyProperty`, paired with `SErrorCannotAssignReadOnly`. Keep this mapping consistent everywhere it applies (e.g., for `ObjectValue`, `ClassValue`, and similar value/descriptor assignments), so the same read-only assignment diagnostic always yields the same suggestion/supported error combination.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 51
File: units/Goccia.AST.Expressions.pas:1213-1216
Timestamp: 2026-03-08T04:34:31.685Z
Learning: In `units/Goccia.AST.Expressions.pas`, `TGocciaThisExpression.Evaluate` must use `AContext.Scope.ThisValue` directly (not `FindThisValue` or `ResolveIdentifier('this')`). `AContext.Scope` is always the scope with `this` properly bound at call time (method call scopes set `ThisValue` on construction). `FindThisValue` is for scope-chain-walking scenarios (e.g., nested contexts), not for evaluating the `this` expression itself. `TGocciaScope.ResolveIdentifier('this')` also accesses `FThisValue` directly rather than calling `FindThisValue`.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 308
File: units/Goccia.VM.pas:3436-3437
Timestamp: 2026-04-15T15:41:47.700Z
Learning: Repo frostney/GocciaScript — units/Goccia.VM.pas:
For TGocciaVM.HasPropertyValue’s “in” operator error path (primitives on the RHS), the project intentionally uses SSuggestCheckNullBeforeAccess for all primitive cases (undefined, null, boolean, number, string). Branching the suggestion by value kind is deferred as low priority; do not re-flag this as an issue unless a dedicated follow-up changes that policy.

Learnt from: CR
Repo: frostney/GocciaScript PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T22:00:59.812Z
Learning: Applies to units/**/*.pas : Values held only by Pascal code (not in any GocciaScript scope) must be protected with `AddTempRoot`/`RemoveTempRoot` for the duration they are needed.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 278
File: units/Goccia.Parser.pas:1071-1111
Timestamp: 2026-04-12T07:39:20.695Z
Learning: Do not free the result of `TGocciaLexer.ScanTokens` (e.g., in `ParseInterpolationExpression` and any other caller). `ScanTokens` returns a reference to the lexer's internal `FTokens: TObjectList<TGocciaToken>` created with `OwnsObjects=True` and freed by `TGocciaLexer`’s destructor. Adding `Tokens.Free` (or otherwise freeing the returned list/object) will cause double-free/access violations; token lifetime is managed entirely by the `TGocciaLexer` instance.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 66
File: units/ScopeMap.pas:0-0
Timestamp: 2026-03-17T22:18:00.984Z
Learning: Do not re-introduce TScopeMap. In frostney/GocciaScript, TScopeMap<TValue> was removed due to performance regressions from its linear-scan Add-as-upsert behavior. Reviews should flag any use of TScopeMap in Units/ScopeMap.pas or elsewhere and ensure bindings use TOrderedStringMap<TLexicalBinding> instead. If reviewing similar Pascal files, prefer avoiding upsert-like add semantics that overwrite existing entries, as they correlated with bytecode performance drops. Ensure changes adhere to the established replacement: TOrderedStringMap<TLexicalBinding>.

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 : All runtime errors must use error helper functions (`ThrowTypeError`, `ThrowRangeError`, `ThrowReferenceError`, `ThrowSyntaxError`, `ThrowError`, `ThrowDataCloneError`) from `Goccia.Values.ErrorHelper.pas`. Never create `TGocciaError` directly. This ensures errors are proper JS Error objects with correct prototype chains, catchable from JavaScript `try...catch`.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 308
File: units/Goccia.Builtins.TestAssertions.pas:541-550
Timestamp: 2026-04-15T15:41:15.873Z
Learning: In the GocciaScript test framework (notably in units/Goccia.Builtins.TestAssertions.pas and units/Goccia.Error.Detail.pas), `ErrorHelper.ThrowTypeError(msg, suggestion)` should populate `TGocciaThrowValue.FSuggestion`. `FormatThrowDetail` forwards this suggestion to `FormatErrorWithSourceContext` only when source context (stack trace) is available. For native test-framework errors thrown without source context (e.g., from `TGocciaParameterizedRegistrationFunction.Call` or validation helpers inside `TGocciaTestAssertions`), the error formatting is intentionally the stack-trace-only path that omits the suggestion. When reviewing `runTests()` output, do not flag missing suggestion text for these internal, source-context-less test-framework paths, since this behavior is by design.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 306
File: TestRunner.dpr:634-636
Timestamp: 2026-04-15T06:27:53.198Z
Learning: In frostney/GocciaScript (PR `#306`, TestRunner.dpr), the `WriteLn` calls inside the error-handling paths of `RunGocciaScriptInterpreted` and `RunGocciaScriptBytecode` (e.g. on EStreamError, TGocciaError, TGocciaThrowValue, and generic Exception) execute on worker threads during parallel directory runs, causing potentially interleaved stdout. This is an accepted trade-off: these paths are rare (script-load failures / uncaught exceptions only), messages include filenames for identification, and a proper output-capture refactor (routing error text through AConsoleOutput/AErrorMessage and printing on the main thread) is deferred to a follow-up. Do NOT re-flag these WriteLn calls in TestRunner.dpr parallel worker context until the output-capture refactor lands.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 58
File: units/Goccia.Runtime.Operations.pas:5959-6132
Timestamp: 2026-03-10T11:17:46.617Z
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. This applies to units/Goccia.Runtime.Operations.pas and related Promise implementations; ensure consistency across related Pas files in the frostney/GocciaScript repository.

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: 302
File: docs/value-system.md:0-0
Timestamp: 2026-04-14T13:25:45.598Z
Learning: In GocciaScript (docs/value-system.md), `TGocciaError` extends Pascal's `Exception`, not `TGocciaValue`. It should not appear in the `TGocciaValue` type hierarchy diagram.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 226
File: units/Goccia.Values.ProxyValue.pas:758-765
Timestamp: 2026-04-08T17:16:34.934Z
Learning: In frostney/GocciaScript, `TGocciaNativeFunctionValue` is a subclass of `TGocciaFunctionBase` (units/Goccia.Values.NativeFunction.pas). An `is TGocciaFunctionBase` check therefore already covers native function targets — do not flag missing separate `TGocciaNativeFunctionValue` branches in fallback call paths (e.g., `ApplyTrap` in `Goccia.Values.ProxyValue.pas`).

Learnt from: frostney
Repo: frostney/GocciaScript PR: 228
File: units/Goccia.Builtins.GlobalReflect.pas:0-0
Timestamp: 2026-04-08T16:50:29.388Z
Learning: In frostney/GocciaScript, `RequireObjectTarget` in `units/Goccia.Builtins.GlobalReflect.pas` accepts only `TGocciaObjectValue` as a valid Reflect target. `TGocciaClassValue` is intentionally rejected because it inherits from `TGocciaValue`, not `TGocciaObjectValue`, in GocciaScript's type hierarchy. There is no `AsObjectValue` helper — class-as-object semantics would require a deeper type hierarchy refactoring and are deferred. Do not re-flag the rejection of `TGocciaClassValue` in `RequireObjectTarget`.

Learnt from: CR
Repo: frostney/GocciaScript PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T22:00:59.812Z
Learning: Applies to units/**/*.pas : Arrow functions (`(x) => x + 1`) are AST: `TGocciaArrowFunctionExpression`, Runtime: `TGocciaArrowFunctionValue`. They always inherit `this` from their lexical (closure) scope via `BindThis` override. Shorthand methods (`method() { ... }`) are AST: `TGocciaMethodExpression`, Runtime: `TGocciaFunctionValue`. They receive call-site `this` from the receiver. `this` binding is resolved via virtual dispatch on `TGocciaFunctionValue.BindThis` — no boolean flags or runtime branches.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 302
File: docs/errors.md:0-0
Timestamp: 2026-04-14T15:13:09.996Z
Learning: In frostney/GocciaScript, `SuppressedError` intentionally supports a `cause` property via a 4th constructor argument: `new SuppressedError(error, suppressed, message, options)`. This is a GocciaScript-specific extension beyond the standard ECMAScript spec (MDN states SuppressedError does not accept an options/cause argument). The implementation is in `Goccia.Builtins.Globals.pas` via `AArgs.GetElement(3)`. Do NOT flag GocciaScript's SuppressedError cause support as non-standard or incorrect.

Learnt from: CR
Repo: frostney/GocciaScript PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T22:00:59.812Z
Learning: Applies to units/**/*.pas : `type`/`interface` declarations and `import type`/`export type` must produce `TGocciaEmptyStatement` (no-op at runtime). Access modifiers (`public`, `protected`, `private`, `readonly`, `override`, `abstract`) in class bodies must be consumed and discarded.

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: CR
Repo: frostney/GocciaScript PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-08T04:38:51.694Z
Learning: Applies to **/*.pas : Use `Goccia.Keywords.Reserved` and `Goccia.Keywords.Contextual` constants instead of hardcoded string literals when referencing JavaScript keywords (e.g., `KEYWORD_THIS`, `KEYWORD_SUPER`, `KEYWORD_GET`).

Learnt from: frostney
Repo: frostney/GocciaScript PR: 299
File: TestRunner.dpr:132-139
Timestamp: 2026-04-14T13:53:18.321Z
Learning: In frostney/GocciaScript CLI tool sources, the duplicated `EscapeJSONString` helper (escaping only `\`, `"`, `\n`, `\r`, `\t`, leaving other C0 control characters U+0000–U+001F unescaped) is tracked for consolidation into a shared helper and full fix in issue `#303`. During code review, do not re-flag missing control-character escaping in individual per-tool `EscapeJSONString` copies (e.g., TestRunner.dpr, BenchmarkRunner.dpr) until the consolidation PR landing issue `#303` is resolved.

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: 53
File: souffle/Souffle.Wasm.Emitter.pas:1467-1477
Timestamp: 2026-03-09T17:21:38.193Z
Learning: In this codebase (Frostney/GocciaScript), for Pascal/Delphi (FPC) files using AnsiString with {$mode delphi} {H+}, treat String as byte-indexed. Length(str) returns byte count and Byte(str[I]) is a no-op cast. Do not replace per-byte copy loops like Data[I-1] := Byte(AStr[I]) with TEncoding.GetBytes, since TEncoding is a Delphi RTL feature unavailable in FPC's SysUtils and would break the build. Apply this guidance to all .pas files under the project; ensure manual per-byte copying logic remains correct for UTF-8 content encoded in AnsiString, and avoid introducing RTL dependencies not supported by FPC.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 63
File: units/Goccia.Runtime.Operations.pas:0-0
Timestamp: 2026-03-11T01:50:11.071Z
Learning: Replace hardcoded string literals 'now', 'toJSON', and 'timeOrigin' with the new constants from Goccia.Constants.PropertyNames (PROP_NOW, PROP_TO_JSON, PROP_TIME_ORIGIN). Apply this refactor across the repository wherever these literals are used, including but not limited to units/Goccia.Runtime.Operations.pas and units/Goccia.Builtins.Performance.pas. Use a project-wide search (e.g., regex or code navigation) to identify all occurrences, update to the respective constants, and adjust imports or uses accordingly. Ensure all references compile and behavior remains unchanged.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 63
File: units/Goccia.Runtime.Operations.pas:7514-7544
Timestamp: 2026-03-11T08:22:20.482Z
Learning: When bridging a native namespace object to a TSouffleRecord (e.g., performance) in units/Goccia.Runtime.Operations.pas, always set Result.Delegate := ARuntime.VM.RecordDelegate after TSouffleRecord.Create(...). This ensures Object.prototype methods (toString/hasOwnProperty/valueOf/toLocaleString) work in bytecode mode and NativeRecordToString honors Symbol.toStringTag. Apply this pattern to BuildPerformanceRecord and any future bridged records.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 66
File: units/HashMap.pas:266-309
Timestamp: 2026-03-17T22:17:37.410Z
Learning: Do not flag enumeration in THashMap<TKey, TValue> as concurrent-modification unsafe. The no-mutation contract is maintained during enumeration: GetEnumerator shares the internal FSlots array (no per-call allocation) and only mutation occurs via destructor or Clear, not during active iteration. This pattern should be recognized across similar Pascal units/pascal collection implementations; when reviewing code, ensure no concurrent-modification warnings are raised for shared internal structures during enumeration if the no-mutation contract is preserved.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 66
File: units/OrderedMap.pas:0-0
Timestamp: 2026-03-17T22:17:55.588Z
Learning: Enforce the constraint on TOrderedMap<TKey, TValue> in units/OrderedMap.pas: TKey must be an unmanaged, fixed-size value type (no strings, dynamic arrays, or records with managed/pointer fields). HashKey should use fast paths for SizeOf(TKey)=8 (QWord) and SizeOf(TKey)=4 (Cardinal); KeysEqual should compare via direct PQWord/PCardinal for those sizes. The raw-byte DJB2/CompareMem fallback is dead code for current uses. Document this constraint in a unit-header comment as the enforcement mechanism. Do NOT re-flag the byte-wise hash/equality in future reviews as long as all TOrderedMap specializations use pointer-sized unmanaged keys.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 66
File: units/BaseMap.pas:22-22
Timestamp: 2026-03-18T08:12:24.165Z
Learning: Shared/utility units under the repository (e.g., units/BaseMap.pas, HashMap.pas, OrderedMap.pas, etc.) that are used across multiple parts of the project should not be required to use the Goccia.* prefix. A separate naming convention will be defined in a follow-up issue. Do not flag missing Goccia.* prefix for these shared infrastructure units in reviews.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 66
File: units/BaseMap.pas:22-22
Timestamp: 2026-03-18T08:12:24.165Z
Learning: Do not flag missing Goccia.* prefixes for shared/utility units that are used across multiple parts of the project or can be extracted as standalone infrastructure (e.g., BaseMap.pas, HashMap.pas, OrderedMap.pas, OrderedStringMap.pas, GC, test assertion units). A separate naming convention (to be defined in issue `#104`) should apply to these targets. When reviewing, apply this guideline to all similar shared utility units under the units directory; for one-off implementations outside this scope, continue with the existing naming expectations.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 110
File: units/Goccia.Runtime.Operations.pas:0-0
Timestamp: 2026-03-23T11:25:00.381Z
Learning: In this repo’s GocciaScript Pascal code, when implementing thenable detection/callability checks (e.g., around Await/unwrap logic), always read the thenable’s `then` property using `Goccia.Constants.PropertyNames.PROP_THEN` rather than a string literal. If the value being inspected is a `TSouffleRecord`, unwrap the retrieved `then` property via `UnwrapToGocciaValue` before testing whether it is callable (e.g., in `TGocciaRuntimeOperations.AwaitValue`).

Learnt from: frostney
Repo: frostney/GocciaScript PR: 107
File: units/Goccia.Runtime.Operations.pas:4349-4353
Timestamp: 2026-03-23T12:38:54.412Z
Learning: In Pascal source under units/, when decoding JSON string escape sequences that represent Unicode code points and then converting them to UTF-8, prefer using the existing helper (implemented via CodePointToUTF8) that correctly combines UTF-16 surrogate pairs (\uD800–\uDBFF + \uDC00–\uDFFF) into a single Unicode code point before UTF-8 encoding. Avoid ad-hoc conversions like Char(StrToInt(...)) for code-point→UTF-8 conversion logic; use the helper instead to ensure surrogate pairs are handled correctly.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 109
File: units/Goccia.Runtime.Operations.pas:6337-6344
Timestamp: 2026-03-23T15:57:55.802Z
Learning: In Pascal unit files under units/, any iterator result record objects produced by native iterators (e.g., in routines analogous to NativeMapIteratorNext / NativeSetIteratorNext) must: (1) set `Rec.Delegate := FVM.RecordDelegate`; and (2) set properties using `PROP_VALUE` / `PROP_DONE` constants from `Goccia.Constants.PropertyNames` rather than hard-coded string literals. Apply the same pattern to any future native iterator result objects so they interoperate correctly in bytecode mode (e.g., for `Object.prototype` method behavior) and keep property-name usage consistent via the shared constants.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 136
File: docs/adding-built-in-types.md:105-133
Timestamp: 2026-03-27T14:09:25.371Z
Learning: In frostney/GocciaScript Pascal code, do not flag or suggest refactoring the early-exit singleton initialization guard `if Assigned(FShared) then Exit` when it appears at the top of `InitializePrototype`. The project documentation treats this early-exit form as the canonical/shared-prototype singleton initialization template (i.e., avoid recommending an `if not Assigned(FShared) then ...` wrapping alternative).

Learnt from: frostney
Repo: frostney/GocciaScript PR: 189
File: units/Goccia.JSON.pas:499-526
Timestamp: 2026-04-05T17:33:32.709Z
Learning: In frostney/GocciaScript, when using `TStringBuffer` from the `StringBuffer` unit, treat it as an advanced record (stack-allocated) with automatic cleanup by the compiler. For `TStringBuffer` locals, do not require or flag missing `SB.Free` calls in `try/finally` blocks—no destructor/free call is needed or appropriate. Only look for `Free` when the variable is actually a heap-allocated class instance.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 189
File: units/Goccia.JSON.pas:535-597
Timestamp: 2026-04-05T17:33:35.381Z
Learning: When reviewing Free Pascal code in this repo, treat `TStringBuffer` (from the `StringBuffer` unit) as an advanced record (value type), meaning instances are stack-allocated. Do not report or require `SB.Free` / `SB.FreeAndNil` (or any `Free` call) for variables typed as `TStringBuffer`, and do not treat an empty or no-op `finally` block after `TStringBuffer` usage as an issue. Only expect heap-management calls for actual `class`/reference types; calling `Free` on an advanced record is incorrect.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 200
File: REPL.dpr:0-0
Timestamp: 2026-04-07T17:12:07.324Z
Learning: When reviewing timing/metrics code in the GocciaScript repo (e.g., REPL.dpr, ScriptLoader, TestRunner, BenchmarkRunner, and TGocciaEngine.Execute), follow the established timing convention: capture `StartTime` before the JSX pre-pass (`TGocciaJSXTransformer.Transform`) and `StringListToLFText`, then capture `LexEnd` after `ScanTokens`. The `Lex` duration (`LexEnd - StartTime`) is intended to include the JSX pre-pass because it is treated as part of the source→tokens pipeline. Do not flag this ordering as a bug and do not recommend moving `StartTime` to after the JSX transform, as it would break the bucket semantics.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 201
File: units/Goccia.AST.Expressions.pas:1339-1346
Timestamp: 2026-04-07T21:00:57.707Z
Learning: When reviewing GocciaScript coverage/branch tracking, do not require call-sites to explicitly pre-register binary (two-arm) branches before recording hits. In units/Goccia.Coverage.pas, TGocciaFileCoverage.RecordBranchHit automatically ensures the opposite arm exists via EnsureBranchExists(ALine, AColumn, 1 - ABranchIndex), establishing a zero-hit baseline for the paired arm whenever either arm is first recorded. Therefore, call-sites such as TGocciaConditionalExpression.Evaluate (Goccia.AST.Expressions.pas), EvaluateIf/EvaluateBinary/EvaluateSwitch (Goccia.Evaluator.pas), and OP_JUMP_IF_* handlers (Goccia.VM.pas) should not be flagged for missing a separate RegisterBranch call for binary branches—pairing is handled inside RecordBranchHit.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 201
File: units/Goccia.VM.pas:3547-3561
Timestamp: 2026-04-07T21:01:16.129Z
Learning: When reviewing calls that record branch coverage via `TGocciaFileCoverage.RecordBranchHit(ALine, AColumn, ABranchIndex)` for binary branches (branch indices 0 and 1), do not require an explicit prior `RegisterBranch(...)` at the call site. `RecordBranchHit` internally calls `EnsureBranchExists(ALine, AColumn, 1 - ABranchIndex)` for binary branches, which guarantees both arms exist in the branch list (the untaken arm is pre-registered with zero hits). Therefore VM/evaluator/AST branch recording sites in `Goccia.VM.pas`, `Goccia.Evaluator.pas`, and `Goccia.AST.Expressions.pas` should not be flagged for missing explicit pre-registration before `RecordBranchHit(...)`.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 209
File: units/Goccia.FFI.DynamicLibrary.pas:7-21
Timestamp: 2026-04-08T13:56:59.262Z
Learning: In Free Pascal (FPC), `TLibHandle` is defined in the `System` unit (`TLibHandle = PtrInt`), not in `Dynlibs`. Since `System` is implicitly available in all Pascal units, referencing `TLibHandle` in the interface section (e.g., as a private field type of a class declared in the interface/`interface` section) does not require `Dynlibs` to be listed in the interface `uses` clause. Only ensure `Dynlibs` is included in the implementation `uses` clause when you actually use `LoadLibrary`, `GetProcAddress`, or `FreeLibrary`. Therefore, do not flag a missing `Dynlibs` entry in the interface `uses` clause when the interface section references only `TLibHandle`.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 242
File: AGENTS.md:396-400
Timestamp: 2026-04-09T11:50:01.452Z
Learning: In .pas code, avoid unsafe Int64→Double conversions/workarounds that trigger FPC 3.2.2 miscompilations. Specifically, do not rely on `Double(Int64Var)` when it performs bit reinterpretation (Delphi-mode front-end bug on affected versions/platforms). Also do not use arithmetic patterns like `Int64Var * 1.0` / `Int64Var * Double` that can produce incorrect results near ±2^31 on AArch64. Prefer value-safe conversions via implicit assignment (e.g., `var D: Double; D := SomeInt64`) or implicit promotion through function/constructor parameter passing (e.g., `TGocciaNumberLiteralValue.Create(SomeInt64)`).

Learnt from: frostney
Repo: frostney/GocciaScript PR: 253
File: units/Goccia.Values.Iterator.Concat.pas:84-88
Timestamp: 2026-04-10T12:44:59.067Z
Learning: In frostney/GocciaScript (GocciaScript), `TGocciaFunctionBase(callable).Call(args, thisValue)` is the project-standard way to invoke callable values. During code review, do not flag `TGocciaFunctionBase(...)` casts at call sites as unsafe and do not recommend replacing them with an alternative “generic callable path”—for this codebase, this cast-and-`Call` pattern is the intended generic callable invocation mechanism.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 273
File: units/Goccia.Values.URLValue.pas:465-470
Timestamp: 2026-04-11T17:42:27.330Z
Learning: In this codebase, when implementing or updating a `TGCManagedObject.MarkReferences` override for a held reference, treat `MarkReferences` as the act of marking *and* tracing children. The expected pattern is: after a nil check, call the held reference’s `MarkReferences` implementation directly (e.g., `TGocciaSomeValue(FRef).MarkReferences`) rather than invoking any non-existent separate `Mark(obj)`/`Mark()` helper. Do not suggest “call `Mark(obj)`”; this project’s marking flow starts by setting `FGCMark := FCurrentMark` and then tracing child references from within `MarkReferences` (consistent with `TGocciaMapValue`, `TGocciaSetValue`, `TGocciaArrayValue`, etc.).

Learnt from: frostney
Repo: frostney/GocciaScript PR: 271
File: units/Goccia.Builtins.GlobalObject.pas:104-129
Timestamp: 2026-04-11T19:43:06.627Z
Learning: In frostney/GocciaScript, `TGarbageCollector.AddTempRoot`/`RemoveTempRoot` are set-membership (not reference-counted). In built-in method implementations, ONLY call `AddTempRoot`/`RemoveTempRoot` if the value was freshly boxed by `ToObject` (i.e., the argument was not already a `TGocciaObjectValue`). Do not unconditionally add/remove temp roots for values passed in as method arguments, since a single `RemoveTempRoot(Obj)` will remove the root for all holders and can cause use-after-free on the next GC cycle. Pattern: `if Assigned(TGarbageCollector.Instance) and not (AArgs.GetElement(0) is TGocciaObjectValue) then AddTempRoot(Obj); try ... finally if Assigned(TGarbageCollector.Instance) and not (AArgs.GetElement(0) is TGocciaObjectValue) then RemoveTempRoot(Obj); end;`

Learnt from: frostney
Repo: frostney/GocciaScript PR: 285
File: units/Goccia.Lexer.pas:207-213
Timestamp: 2026-04-12T15:46:28.683Z
Learning: In this Frostney/GocciaScript project (Free Pascal / FPC, including files under units/), do not recommend adding explicit `{$B-}` directives (either inside units or in `Goccia.inc`) to “fix” short-circuit evaluation safety. `{$B-}` is already FPC’s default in all compiler modes (`fpc`, `delphi`, `objfpc`). Treat missing `{$B-}` as expected and do not flag it as a maintainability or safety issue. Only note `{$B-}` overrides if an explicit `{$B+}` is present in the file or via a highly visible project-level `fpc.cfg` setting.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 296
File: units/Goccia.REPL.Formatter.pas:15-29
Timestamp: 2026-04-13T16:53:02.191Z
Learning: For Pascal units in frostney/GocciaScript, enforce `uses`-clause grouping order as follows (with blank lines between groups): (1) System units (e.g., `SysUtils`), (2) Third-party units, then (3) Project units (e.g., `Goccia.*`). Specifically, `StringBuffer` (from `StringBuffer.pas`) must be treated as a Third-party unit and placed in the Third-party group. Do not report a `uses` ordering violation when `StringBuffer` appears between the System group and the `Goccia.*` group, since `./format.pas` and the Lefthook pre-commit hook enforce this grouping.

Learnt from: frostney
Repo: frostney/GocciaScript PR: 302
File: docs/contributing/code-style.md:31-37
Timestamp: 2026-04-14T13:36:54.107Z
Learning: When reviewing Free Pascal (FPC) sources in this project, do not flag the compiler directive `{H+}` as a syntax error or as an inconsistency caused by a missing `$`. In FPC, `{H+}` (short-form) is valid and equivalent to `{$H+}` for enabling long/AnsiStrings. This project’s own `Goccia.inc` and documentation intentionally use `{H+}`, so the reviewer should only consider it an issue if it’s not valid FPC syntax for the surrounding context.

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

github-actions Bot commented Apr 15, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 5358 5358
Tests Passed 5317 ✅ 5358 ✅
Tests Skipped 41 0
Tests Test Duration 867.7ms 819.8ms
Tests Lex 216.0ms 150.0ms
Tests Parse 241.7ms 248.7ms
Tests Compile 159.9ms
Tests Execute 904.4ms 884.6ms
Tests Engine Total 1.36s 1.44s
Benchmarks Total 364 364
Benchmarks Duration 8.31min 7.84min

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 15, 2026

Benchmark Results

364 benchmarks

Interpreted: 🟢 33 improved · 🔴 178 regressed · 153 unchanged · avg -2.6%
Bytecode: 🟢 10 improved · 🔴 261 regressed · 93 unchanged · avg -4.5%

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

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: 6

🧹 Nitpick comments (1)
units/Goccia.Values.Iterator.Generic.pas (1)

172-172: Consider a return()-specific suggestion string for this branch.

At Line 172, the error message is about iterator.return(), but SSuggestIteratorResultObject text refers to next(). A dedicated suggestion constant for return() would make the hint fully consistent.

Possible refinement
-      ThrowTypeError(SErrorIteratorReturnObject, SSuggestIteratorResultObject);
+      ThrowTypeError(SErrorIteratorReturnObject, SSuggestIteratorReturnResultObject);
// Add in units/Goccia.Error.Suggestions.pas
SSuggestIteratorReturnResultObject =
  'iterator return() must return an object with ''done'' and ''value'' properties';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Values.Iterator.Generic.pas` at line 172, The ThrowTypeError
call at ThrowTypeError(SErrorIteratorReturnObject, SSuggestIteratorResultObject)
uses SSuggestIteratorResultObject which references next() instead of return();
create a new suggestion constant (e.g., SSuggestIteratorReturnResultObject) in
units/Goccia.Error.Suggestions.pas with wording specific to iterator.return()
and replace SSuggestIteratorResultObject with SSuggestIteratorReturnResultObject
at the ThrowTypeError site so the hint correctly mentions return() and the
required 'done' and 'value' properties.
🤖 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.Builtins.DisposableStack.pas`:
- Line 99: The ThrowReferenceError call at
ThrowReferenceError(SErrorDisposableStackAlreadyDisposed,
SSuggestDisposableStackThisType) uses SSuggestDisposableStackThisType which is
about receiver type, not disposal state; introduce a new suggestion constant
(e.g., SSuggestDisposableStackNotDisposed or similar) with text like "ensure the
DisposableStack is not disposed before calling this method" and replace
SSuggestDisposableStackThisType in the ThrowReferenceError invocation so
SErrorDisposableStackAlreadyDisposed is paired with the disposed-state-specific
suggestion.

In `@units/Goccia.Builtins.TestAssertions.pas`:
- Around line 541-550: The error messages currently pass GetFunctionName (which
yields "describe"/"test") into the SErrorFunctionExpects* formats, causing
misuse of describe.each/test.each to show the wrong entrypoint; update the three
ThrowTypeError calls that use GetFunctionName (the ones validating
AArguments.GetElement(0) string, AArguments.GetElement(1) function, and FTable
being TGocciaArrayValue) to pass GetFunctionName + '.each' (or otherwise append
".each" to the function name) as the argument so the formatted messages point to
the correct entrypoint.

In `@units/Goccia.Evaluator.pas`:
- Around line 823-825: The null/undefined property-read fallback throws via
ThrowTypeError(Format(SErrorCannotReadPropertyOf, [PropertyName,
Obj.ToStringLiteral.Value])) but omits the suggestion text present in sibling
branches; update this ThrowTypeError invocation in the same block (the branch
that uses SErrorCannotReadPropertyOf, PropertyName and
Obj.ToStringLiteral.Value) to include the same suggestion/hint string used by
the other branches (mirror their Format usage or append the suggestion variable
they use) so the error message includes the helpful suggestion text just like
the siblings.

In `@units/Goccia.Values.TemporalDuration.pas`:
- Line 94: The TypeError thrown in AsDuration currently uses
SSuggestTemporalFromArg for a bad receiver; update the ThrowTypeError call in
AsDuration to use SSuggestTemporalThisType instead of SSuggestTemporalFromArg so
the diagnostic references the receiver (AThisValue) correctly; locate the
ThrowTypeError invocation inside the AsDuration routine and replace the
suggestion symbol with SSuggestTemporalThisType while keeping the same message
prefix (AMethod + ' called on non-Duration').

In `@units/Goccia.Values.TemporalInstant.pas`:
- Line 64: The AsInstant receiver-type check is using the wrong suggestion
constant: when validating AThisValue and throwing via ThrowTypeError(AMethod + '
called on non-Instant', SSuggestTemporalFromArg) you must use
SSuggestTemporalThisType instead of SSuggestTemporalFromArg; locate the wrong
ThrowTypeError call in AsInstant and swap the suggestion constant so the
wrong-receiver (`this`) diagnostic uses SSuggestTemporalThisType.

In `@units/Goccia.Values.TemporalPlainTime.pas`:
- Line 77: The type-guard for AsPlainTime currently throws
ThrowTypeError(AMethod + ' called on non-PlainTime', SSuggestTemporalFromArg)
while validating the receiver (AThisValue); change the suggestion token to
SSuggestTemporalThisType so the wrong-receiver error uses the receiver-specific
hint. Locate the ThrowTypeError call in the AsPlainTime/type-guard code path and
replace SSuggestTemporalFromArg with SSuggestTemporalThisType, keeping AMethod
and the rest of the call intact.

---

Nitpick comments:
In `@units/Goccia.Values.Iterator.Generic.pas`:
- Line 172: The ThrowTypeError call at
ThrowTypeError(SErrorIteratorReturnObject, SSuggestIteratorResultObject) uses
SSuggestIteratorResultObject which references next() instead of return(); create
a new suggestion constant (e.g., SSuggestIteratorReturnResultObject) in
units/Goccia.Error.Suggestions.pas with wording specific to iterator.return()
and replace SSuggestIteratorResultObject with SSuggestIteratorReturnResultObject
at the ThrowTypeError site so the hint correctly mentions return() and the
required 'done' and 'value' properties.
🪄 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: 00e5d81c-0fdf-4610-a8a2-16997e821912

📥 Commits

Reviewing files that changed from the base of the PR and between 7532357 and 838db14.

📒 Files selected for processing (81)
  • .gitignore
  • BenchmarkRunner.dpr
  • REPL.dpr
  • ScriptLoader.dpr
  • TestRunner.dpr
  • units/Goccia.Application.pas
  • units/Goccia.Builtins.DisposableStack.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.GlobalArrayBuffer.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Builtins.Temporal.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Error.Suggestions.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Scope.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.URI.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.Iterator.Zip.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.SymbolValue.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Values.TemporalPlainDate.pas
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas
✅ Files skipped from review due to trivial changes (18)
  • .gitignore
  • units/Goccia.Values.FunctionValue.pas
  • REPL.dpr
  • ScriptLoader.dpr
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • TestRunner.dpr
  • units/Goccia.VM.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.TemporalPlainDate.pas
🚧 Files skipped from review as they are similar to previous changes (42)
  • units/Goccia.Application.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.Iterator.Zip.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.URI.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Values.PromiseValue.pas
  • BenchmarkRunner.dpr
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Error.Suggestions.pas

Comment thread units/Goccia.Builtins.DisposableStack.pas Outdated
Comment thread units/Goccia.Builtins.TestAssertions.pas
Comment thread units/Goccia.Evaluator.pas Outdated
Comment thread units/Goccia.Values.TemporalDuration.pas Outdated
Comment thread units/Goccia.Values.TemporalInstant.pas Outdated
Comment thread units/Goccia.Values.TemporalPlainTime.pas Outdated
@frostney frostney force-pushed the t3code/align-error-suggestions branch from 838db14 to 7581e1e Compare April 15, 2026 21:19
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@units/Goccia.Builtins.Temporal.pas`:
- Around line 352-353: The code currently calls ThrowTypeError when ISO
parsing/validation fails (e.g., the TryParseISODateTime failure handling using
TGocciaStringLiteralValue(Arg).Value with SErrorInvalidISOInstant /
SSuggestTemporalISOFormat); per the Temporal spec these are value-domain errors
and must raise a RangeError. Replace the ThrowTypeError calls with
ThrowRangeError at this site and the other listed parsing/validation failure
sites (the analogous failure points in Instant.from, PlainDate.from,
PlainTime.from, PlainDateTime.from, PlainMonthDay.from and the other locations
you noted) so that parsing/field-validation failures use ThrowRangeError instead
of ThrowTypeError. Ensure the same error message arguments (e.g.,
SErrorInvalidISOInstant, SSuggestTemporalISOFormat) are preserved when switching
to ThrowRangeError.
🪄 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: f4b83321-adee-4438-8522-e62b950e7267

📥 Commits

Reviewing files that changed from the base of the PR and between 838db14 and 7581e1e.

📒 Files selected for processing (81)
  • .gitignore
  • BenchmarkRunner.dpr
  • REPL.dpr
  • ScriptLoader.dpr
  • TestRunner.dpr
  • units/Goccia.Application.pas
  • units/Goccia.Builtins.DisposableStack.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.GlobalArrayBuffer.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Builtins.Temporal.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Error.Suggestions.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Scope.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.URI.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.Iterator.Zip.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.SymbolValue.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Values.TemporalPlainDate.pas
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas
✅ Files skipped from review due to trivial changes (21)
  • .gitignore
  • ScriptLoader.dpr
  • units/Goccia.Application.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.ImportMeta.pas
  • REPL.dpr
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.DisposalTracker.pas
  • TestRunner.dpr
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Builtins.GlobalPromise.pas
🚧 Files skipped from review as they are similar to previous changes (41)
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • BenchmarkRunner.dpr
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.VM.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Values.TemporalPlainDate.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Error.Suggestions.pas

Comment thread units/Goccia.Builtins.Temporal.pas
@frostney frostney force-pushed the t3code/align-error-suggestions branch from 7581e1e to 6f3e3b6 Compare April 16, 2026 06:19
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

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

3833-3846: ⚠️ Potential issue | 🟠 Major

Box non-null primitives before object destructuring.

This branch rejects every non-TGocciaObjectValue, which breaks valid patterns like const { length } = 'abc' or { toFixed } = 1. Per JavaScript semantics, object-pattern destructuring must only reject null/undefined; other primitives require coercion to their wrapper objects before property lookup (e.g., String('abc') for strings, Number(1) for numbers). Array destructuring already handles string primitives correctly (lines ~3751–3774); object destructuring should apply the same coercion strategy.

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

In `@units/Goccia.Evaluator.pas` around lines 3833 - 3846, The current check in
the object-destructuring path rejects any non-TGocciaObjectValue (using
TGocciaNullLiteralValue/TGocciaUndefinedLiteralValue checks and ThrowTypeError
with SErrorCannotDestructure/SErrorCannotDestructureType), which is wrong for
primitives; change it to only throw for TGocciaNullLiteralValue or
TGocciaUndefinedLiteralValue and for other primitive values box/coerce them into
their object wrappers before continuing (i.e., replace the unconditional error
branch so that when AValue is not TGocciaObjectValue but also not null/undefined
you create the appropriate boxed object value and assign it to ObjectValue,
leaving ThrowTypeError only for null/undefined cases), preserving existing error
messages and the subsequent use of ObjectValue.
♻️ Duplicate comments (1)
units/Goccia.Builtins.Temporal.pas (1)

352-353: ⚠️ Potential issue | 🟠 Major

Use RangeError for these Temporal value-domain failures.

These branches already accepted the argument type and only fail during ISO parsing or numeric-domain validation, so callers currently observe the wrong error class.

Suggested fix pattern
-      ThrowTypeError(SErrorInvalidISOInstant, SSuggestTemporalISOFormat);
+      ThrowRangeError(SErrorInvalidISOInstant, SSuggestTemporalISOFormat);

Apply the same swap at the other listed value-domain failure sites.

For the JavaScript Temporal API/proposal, should invalid ISO string/value-domain failures in Temporal.Instant.from/compare, Temporal.PlainDate.from/compare, Temporal.PlainTime.from/compare, Temporal.PlainDateTime.from/compare, and month-out-of-range errors in Temporal.PlainMonthDay.from throw RangeError or TypeError?

Also applies to: 406-407, 496-496, 565-565, 643-643, 687-687, 775-775, 819-819, 1063-1064

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

In `@units/Goccia.Builtins.Temporal.pas` around lines 352 - 353, The branch
currently calls ThrowTypeError when ISO parsing fails (using TryParseISODateTime
with TGocciaStringLiteralValue(Arg).Value) but should raise a RangeError;
replace the ThrowTypeError(SErrorInvalidISOInstant, SSuggestTemporalISOFormat)
call with ThrowRangeError(...) preserving the same message arguments, and apply
the same swap (TypeError -> RangeError) at the other value-domain failure sites
referenced (lines around 406-407, 496, 565, 643, 687, 775, 819, 1063-1064) so
all TryParse* / numeric-domain validation failures use ThrowRangeError instead
of ThrowTypeError.
🧹 Nitpick comments (3)
units/Goccia.Values.TemporalPlainYearMonth.pas (1)

159-164: Use PROP_* constants in the touched Temporal lookup paths.

This file still hardcodes year, month, and day in the updated property-access flow, and CoercePlainYearMonth still spells out the year/month property names directly in its error text. Since this unit is already being touched for diagnostic cleanup, it would be better to switch these lookups/messages to PROP_YEAR, PROP_MONTH, and PROP_DAY here as well.

♻️ Suggested cleanup
+  Goccia.Constants.PropertyNames,
   Goccia.Error.Messages,
   Goccia.Error.Suggestions,
@@
-    V := Obj.GetProperty('year');
+    V := Obj.GetProperty(PROP_YEAR);
     if (V = nil) or (V is TGocciaUndefinedLiteralValue) then
-      ThrowTypeError(AMethod + ' requires year and month properties', SSuggestTemporalFromArg);
-    VMonth := Obj.GetProperty('month');
+      ThrowTypeError(
+        Format('%s requires %s and %s properties', [AMethod, PROP_YEAR, PROP_MONTH]),
+        SSuggestTemporalFromArg);
+    VMonth := Obj.GetProperty(PROP_MONTH);
     if (VMonth = nil) or (VMonth is TGocciaUndefinedLiteralValue) then
-      ThrowTypeError(AMethod + ' requires year and month properties', SSuggestTemporalFromArg);
+      ThrowTypeError(
+        Format('%s requires %s and %s properties', [AMethod, PROP_YEAR, PROP_MONTH]),
+        SSuggestTemporalFromArg);
@@
-  NewYear := GetFieldOr('year', YM.FYear);
-  NewMonth := GetFieldOr('month', YM.FMonth);
+  NewYear := GetFieldOr(PROP_YEAR, YM.FYear);
+  NewMonth := GetFieldOr(PROP_MONTH, YM.FMonth);
@@
-  V := Obj.GetProperty('day');
+  V := Obj.GetProperty(PROP_DAY);

Based on learnings: In frostney/GocciaScript, all Temporal value unit implementations must use Goccia.Constants.PropertyNames::{PROP_YEAR, PROP_MONTH, PROP_MONTH_CODE, PROP_DAY, PROP_CALENDAR_ID} for Temporal property access and related error messages.

Also applies to: 333-337, 566-568

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

In `@units/Goccia.Values.TemporalPlainYearMonth.pas` around lines 159 - 164, The
code hardcodes 'year'/'month'/'day' property names and messages in
CoercePlainYearMonth and related lookup paths; replace direct string literals
used in Obj.GetProperty and in the ThrowTypeError messages with the constants
PROP_YEAR, PROP_MONTH, and PROP_DAY from Goccia.Constants.PropertyNames (and use
PROP_MONTH_CODE / PROP_CALENDAR_ID where applicable) so property access and
diagnostic text use the canonical constants; update all occurrences in the unit
(including the shown GetProperty calls and the other locations around the
referenced ranges) to reference the constants instead of raw strings while
preserving AMethod and SSuggestTemporalFromArg in the ThrowTypeError calls.
units/Goccia.Evaluator.pas (1)

3519-3524: Split the Symbol + shortcut by string-vs-number coercion.

This fast path always throws SErrorSymbolToString, but + only takes the string-coercion route when one operand is already a string. Cases like sym + 1 should stay aligned with the normal numeric path and report the Symbol-to-number failure instead of the string one.

💡 Proposed adjustment
-          if (LeftVal is TGocciaSymbolValue) or (RightVal is TGocciaSymbolValue) then
-            ThrowTypeError(SErrorSymbolToString, SSuggestSymbolNoImplicitConversion);
-          if (LeftVal is TGocciaStringLiteralValue) or (RightVal is TGocciaStringLiteralValue) then
-            Result := TGocciaStringLiteralValue.Create(LeftVal.ToStringLiteral.Value + RightVal.ToStringLiteral.Value)
-          else
-            Result := TGocciaNumberLiteralValue.Create(LeftVal.ToNumberLiteral.Value + RightVal.ToNumberLiteral.Value);
+          if (LeftVal is TGocciaStringLiteralValue) or
+             (RightVal is TGocciaStringLiteralValue) then
+          begin
+            if (LeftVal is TGocciaSymbolValue) or (RightVal is TGocciaSymbolValue) then
+              ThrowTypeError(SErrorSymbolToString, SSuggestSymbolNoImplicitConversion);
+            Result := TGocciaStringLiteralValue.Create(
+              LeftVal.ToStringLiteral.Value + RightVal.ToStringLiteral.Value);
+          end
+          else
+          begin
+            if (LeftVal is TGocciaSymbolValue) or (RightVal is TGocciaSymbolValue) then
+              ThrowTypeError(SErrorSymbolToNumber, SSuggestSymbolNoImplicitConversion);
+            Result := TGocciaNumberLiteralValue.Create(
+              LeftVal.ToNumberLiteral.Value + RightVal.ToNumberLiteral.Value);
+          end;

Based on learnings, TGocciaSymbolValue.ToNumberLiteral must throw TypeError, while implicit string coercion in the + operator must handle symbols at the operator level.

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

In `@units/Goccia.Evaluator.pas` around lines 3519 - 3524, The current fast-path
always throws SErrorSymbolToString when either operand is TGocciaSymbolValue;
instead, remove that early throw and only raise the symbol-to-string TypeError
when taking the string-concatenation branch. Concretely: in the '+' handling
code (where TGocciaStringLiteralValue, TGocciaNumberLiteralValue,
TGocciaSymbolValue are inspected), don't call ThrowTypeError when a symbol is
present up-front; first test whether either operand is a
TGocciaStringLiteralValue — if so, perform ToStringLiteral on both operands and
if any ToStringLiteral detects a symbol then call
ThrowTypeError(SErrorSymbolToString, SSuggestSymbolNoImplicitConversion);
otherwise take the numeric path and call ToNumberLiteral on both operands so
TGocciaSymbolValue.ToNumberLiteral can raise the proper symbol-to-number
TypeError.
units/Goccia.Values.ErrorHelper.pas (1)

34-39: Consider adding ThrowInvalidCharacterError suggestion overload for API symmetry.

Most throw helpers now have (AMessage, ASuggestion) overloads, but InvalidCharacterError remains message-only. Optional, but adding it would keep the helper surface consistent.

♻️ Suggested consistency patch
-{ Raises a TGocciaThrowValue with an InvalidCharacterError (DOMException with code 5) }
-procedure ThrowInvalidCharacterError(const AMessage: string);
+{ Raises a TGocciaThrowValue with an InvalidCharacterError (DOMException with code 5) }
+procedure ThrowInvalidCharacterError(const AMessage: string); overload;
+procedure ThrowInvalidCharacterError(const AMessage, ASuggestion: string); overload;
 procedure ThrowInvalidCharacterError(const AMessage: string);
 var
   ErrorObj: TGocciaObjectValue;
 begin
   ErrorObj := CreateErrorObject(INVALID_CHARACTER_ERROR_NAME, AMessage);
   ErrorObj.HasErrorData := False;
   ErrorObj.AssignProperty(PROP_CODE, TGocciaNumberLiteralValue.Create(5));
   if Assigned(GDOMExceptionProto) then
     ErrorObj.Prototype := GDOMExceptionProto;
   raise TGocciaThrowValue.Create(ErrorObj);
 end;
+
+procedure ThrowInvalidCharacterError(const AMessage, ASuggestion: string);
+var
+  ErrorObj: TGocciaObjectValue;
+begin
+  ErrorObj := CreateErrorObject(INVALID_CHARACTER_ERROR_NAME, AMessage);
+  ErrorObj.HasErrorData := False;
+  ErrorObj.AssignProperty(PROP_CODE, TGocciaNumberLiteralValue.Create(5));
+  if Assigned(GDOMExceptionProto) then
+    ErrorObj.Prototype := GDOMExceptionProto;
+  raise TGocciaThrowValue.Create(ErrorObj, ASuggestion);
+end;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Values.ErrorHelper.pas` around lines 34 - 39, Add a second
overload for ThrowInvalidCharacterError that accepts (const AMessage,
ASuggestion: string) to mirror the other throw helpers; declare the new overload
alongside the existing procedure and implement it to construct/raise the same
TGocciaThrowValue/DOMException (code 5) as the message-only variant but include
ASuggestion in the thrown value (same pattern used by ThrowURIError(const
AMessage, ASuggestion) and other helpers) so the API surface is symmetric.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@units/Goccia.Values.SharedArrayBufferValue.pas`:
- Around line 149-157: The code currently calls
ThrowRangeError(SErrorInvalidSharedArrayBufferLength,
SSuggestSharedArrayBufferThisType) when IntegerIndex is out of range (and at the
other noted site), which incorrectly uses the “wrong receiver” hint; change
these calls to either omit the suggestion parameter or pass a new dedicated hint
constant (e.g. SSuggestSharedArrayBufferLength or
SSuggestSharedArrayBufferSpecies) instead of SSuggestSharedArrayBufferThisType
so that ThrowRangeError is invoked with a correct suggestion for length/species
failures; update both occurrences around IntegerIndex and the other referenced
location (253-254) to use the new or no-suggestion identifier consistently.

In `@units/Goccia.Values.TemporalPlainDateTime.pas`:
- Around line 144-145: Replace the incorrect ThrowTypeError calls with
ThrowRangeError in the Temporal string-parsing failure branches: specifically
change the ThrowTypeError used when
TryParseISODateTime(TGocciaStringLiteralValue(AValue).Value, DateRec, TimeRec)
fails (the call that uses SErrorTemporalInvalidISOStringFor and
SSuggestTemporalISOFormat) and the three other ThrowTypeError occurrences that
are raised for invalid ISO/time/duration string parsing in this unit; keep the
existing error message formatting and arguments but use ThrowRangeError instead
of ThrowTypeError.

---

Outside diff comments:
In `@units/Goccia.Evaluator.pas`:
- Around line 3833-3846: The current check in the object-destructuring path
rejects any non-TGocciaObjectValue (using
TGocciaNullLiteralValue/TGocciaUndefinedLiteralValue checks and ThrowTypeError
with SErrorCannotDestructure/SErrorCannotDestructureType), which is wrong for
primitives; change it to only throw for TGocciaNullLiteralValue or
TGocciaUndefinedLiteralValue and for other primitive values box/coerce them into
their object wrappers before continuing (i.e., replace the unconditional error
branch so that when AValue is not TGocciaObjectValue but also not null/undefined
you create the appropriate boxed object value and assign it to ObjectValue,
leaving ThrowTypeError only for null/undefined cases), preserving existing error
messages and the subsequent use of ObjectValue.

---

Duplicate comments:
In `@units/Goccia.Builtins.Temporal.pas`:
- Around line 352-353: The branch currently calls ThrowTypeError when ISO
parsing fails (using TryParseISODateTime with
TGocciaStringLiteralValue(Arg).Value) but should raise a RangeError; replace the
ThrowTypeError(SErrorInvalidISOInstant, SSuggestTemporalISOFormat) call with
ThrowRangeError(...) preserving the same message arguments, and apply the same
swap (TypeError -> RangeError) at the other value-domain failure sites
referenced (lines around 406-407, 496, 565, 643, 687, 775, 819, 1063-1064) so
all TryParse* / numeric-domain validation failures use ThrowRangeError instead
of ThrowTypeError.

---

Nitpick comments:
In `@units/Goccia.Evaluator.pas`:
- Around line 3519-3524: The current fast-path always throws
SErrorSymbolToString when either operand is TGocciaSymbolValue; instead, remove
that early throw and only raise the symbol-to-string TypeError when taking the
string-concatenation branch. Concretely: in the '+' handling code (where
TGocciaStringLiteralValue, TGocciaNumberLiteralValue, TGocciaSymbolValue are
inspected), don't call ThrowTypeError when a symbol is present up-front; first
test whether either operand is a TGocciaStringLiteralValue — if so, perform
ToStringLiteral on both operands and if any ToStringLiteral detects a symbol
then call ThrowTypeError(SErrorSymbolToString,
SSuggestSymbolNoImplicitConversion); otherwise take the numeric path and call
ToNumberLiteral on both operands so TGocciaSymbolValue.ToNumberLiteral can raise
the proper symbol-to-number TypeError.

In `@units/Goccia.Values.ErrorHelper.pas`:
- Around line 34-39: Add a second overload for ThrowInvalidCharacterError that
accepts (const AMessage, ASuggestion: string) to mirror the other throw helpers;
declare the new overload alongside the existing procedure and implement it to
construct/raise the same TGocciaThrowValue/DOMException (code 5) as the
message-only variant but include ASuggestion in the thrown value (same pattern
used by ThrowURIError(const AMessage, ASuggestion) and other helpers) so the API
surface is symmetric.

In `@units/Goccia.Values.TemporalPlainYearMonth.pas`:
- Around line 159-164: The code hardcodes 'year'/'month'/'day' property names
and messages in CoercePlainYearMonth and related lookup paths; replace direct
string literals used in Obj.GetProperty and in the ThrowTypeError messages with
the constants PROP_YEAR, PROP_MONTH, and PROP_DAY from
Goccia.Constants.PropertyNames (and use PROP_MONTH_CODE / PROP_CALENDAR_ID where
applicable) so property access and diagnostic text use the canonical constants;
update all occurrences in the unit (including the shown GetProperty calls and
the other locations around the referenced ranges) to reference the constants
instead of raw strings while preserving AMethod and SSuggestTemporalFromArg in
the ThrowTypeError calls.
🪄 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: d2792b78-519e-46c5-9488-b00a2fd43fcc

📥 Commits

Reviewing files that changed from the base of the PR and between 7581e1e and 6f3e3b6.

📒 Files selected for processing (81)
  • .gitignore
  • BenchmarkRunner.dpr
  • REPL.dpr
  • ScriptLoader.dpr
  • TestRunner.dpr
  • units/Goccia.Application.pas
  • units/Goccia.Builtins.DisposableStack.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.GlobalArrayBuffer.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.JSON5.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.Math.pas
  • units/Goccia.Builtins.Performance.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Builtins.TOML.pas
  • units/Goccia.Builtins.Temporal.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Builtins.YAML.pas
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Error.Detail.pas
  • units/Goccia.Error.Messages.pas
  • units/Goccia.Error.Suggestions.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Evaluator.pas
  • units/Goccia.ImportMeta.pas
  • units/Goccia.Scope.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.URI.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.ArrayBufferValue.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Values.Error.pas
  • units/Goccia.Values.ErrorHelper.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.FunctionBase.pas
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Values.Iterator.Zip.pas
  • units/Goccia.Values.IteratorValue.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Values.ProxyValue.pas
  • units/Goccia.Values.SharedArrayBufferValue.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.SymbolValue.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Values.TemporalPlainDate.pas
  • units/Goccia.Values.TemporalPlainDateTime.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.TemporalPlainTime.pas
  • units/Goccia.Values.TemporalPlainYearMonth.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.TextDecoderValue.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Values.TypedArrayValue.pas
  • units/Goccia.Values.URLSearchParamsValue.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas
✅ Files skipped from review due to trivial changes (10)
  • units/Goccia.Builtins.Performance.pas
  • TestRunner.dpr
  • units/Goccia.Values.FunctionBase.pas
  • .gitignore
  • units/Goccia.DisposalTracker.pas
  • units/Goccia.Values.Uint8ArrayEncoding.pas
  • units/Goccia.Values.URLValue.pas
  • units/Goccia.Builtins.GlobalRegExp.pas
  • units/Goccia.Builtins.GlobalSymbol.pas
  • units/Goccia.Error.Messages.pas
🚧 Files skipped from review as they are similar to previous changes (44)
  • units/Goccia.Values.FunctionValue.pas
  • units/Goccia.Utils.Arrays.pas
  • units/Goccia.Application.pas
  • units/Goccia.Values.Iterator.Concat.pas
  • units/Goccia.Values.Iterator.Lazy.pas
  • units/Goccia.Evaluator.Assignment.pas
  • units/Goccia.Values.ObjectPropertyDescriptor.pas
  • units/Goccia.Evaluator.TypeOperations.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.MapValue.pas
  • units/Goccia.Builtins.GlobalMap.pas
  • units/Goccia.Builtins.JSONL.pas
  • units/Goccia.Builtins.DisposableStack.pas
  • units/Goccia.Values.ToPrimitive.pas
  • units/Goccia.Builtins.GlobalFFI.pas
  • units/Goccia.Values.NumberObjectValue.pas
  • units/Goccia.Values.Iterator.Generic.pas
  • units/Goccia.Builtins.GlobalObject.pas
  • units/Goccia.Builtins.GlobalString.pas
  • units/Goccia.Values.Error.pas
  • BenchmarkRunner.dpr
  • units/Goccia.Values.TemporalInstant.pas
  • units/Goccia.Builtins.GlobalArray.pas
  • units/Goccia.Builtins.Globals.pas
  • units/Goccia.Values.PromiseValue.pas
  • units/Goccia.Builtins.GlobalReflect.pas
  • units/Goccia.Builtins.GlobalProxy.pas
  • units/Goccia.Temporal.Options.pas
  • units/Goccia.Builtins.JSON.pas
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Values.TextEncoderValue.pas
  • units/Goccia.Values.StringObjectValue.pas
  • units/Goccia.Values.ObjectValue.pas
  • units/Goccia.Values.ClassValue.pas
  • units/Goccia.Builtins.GlobalPromise.pas
  • units/Goccia.Scope.pas
  • units/Goccia.Evaluator.Arithmetic.pas
  • units/Goccia.Values.TemporalPlainMonthDay.pas
  • units/Goccia.Values.TemporalZonedDateTime.pas
  • units/Goccia.Values.ArrayValue.pas
  • units/Goccia.Builtins.TestAssertions.pas
  • units/Goccia.Values.TemporalDuration.pas
  • units/Goccia.VM.pas
  • units/Goccia.Error.Suggestions.pas

Comment thread units/Goccia.Values.SharedArrayBufferValue.pas Outdated
Comment thread units/Goccia.Values.TemporalPlainDateTime.pas
- Thread suggestion hints through throw formatting
- Replace ad hoc error strings with shared message constants
- Add shell helper for Pascal assembly build step
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Refactoring, CI, tooling, cleanup

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant