Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

addInitializer silently ignores a non-callable argument instead of throwing TypeError

Closed Beginner friendly
#1,668 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

A pull request for this has already been merged.

  • #1671 by @frostney — merged

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
javascript
Domain
compilers

Research direction

The check lives in TGocciaInitializerCollector.AddInitializer in source/units/Goccia.Evaluator.Decorators.pas, which both execution modes share. Read how it handles the first argument, then find where the codebase raises TypeErrors elsewhere so you raise one the same way. Done means all six cases in the issue throw a TypeError in both --mode=bytecode and --mode=interpreted, and regression cases are added to tests/language/decorators/add-initializer.js.

Written by the indexing model from the issue text.

Description

bug engine spec compliance

Summary

context.addInitializer(x) silently ignores a non-callable x (a number, string, object, undefined, or no argument) in both modes. The decorators proposal requires a TypeError.

Why

Decorators proposal (tc39/ecma262 PR #2417 at 59003ed6), CreateAddInitializerFunction (sec-createaddinitializerfunction), step 2 of the addInitializer closure:

If IsCallable(initializer) is false, throw a TypeError exception.

TGocciaInitializerCollector.AddInitializer (source/units/Goccia.Evaluator.Decorators.pas), which both modes use, only appends the value when it is callable, and otherwise returns undefined:

if (AArgs.Length > 0) and AArgs.GetElement(0).IsCallable then
begin
  SetLength(FInitializers, Length(FInitializers) + 1);
  FInitializers[High(FInitializers)] := AArgs.GetElement(0);
end;
Result := TGocciaUndefinedLiteralValue.UndefinedValue;

Current behavior

main at adabb564. Each of these prints no throw in both --mode=bytecode and --mode=interpreted:

const t = (label, f) => { try { f(); console.log(label, "no throw"); } catch (e) { console.log(label, e.constructor.name); } };
t("class", () => { const d = (c, ctx) => { ctx.addInitializer(42); }; @d class A {} });
t("method", () => { const d = (m, ctx) => { ctx.addInitializer("x"); }; class A { @d m() {} } });
t("field", () => { const d = (v, ctx) => { ctx.addInitializer({}); }; class A { @d x = 1; } });
t("static method", () => { const d = (m, ctx) => { ctx.addInitializer(undefined); }; class A { @d static m() {} } });
t("no argument", () => { const d = (c, ctx) => { ctx.addInitializer(); }; @d class A {} });

Decorators are not in Node.js 24, so there is no engine reference here; the expected behavior comes from the proposal text above.

Expected behavior

Each call throws a TypeError from addInitializer, which propagates out of the decorator and out of the class definition. This applies to every element kind, static and non-static, in both modes.

Scope notes

  • The fix belongs in TGocciaInitializerCollector.AddInitializer, and is the same change in both modes.
  • The step-1 sibling, addInitializer called after decoration has finished, is a use-after-free crash and is filed separately as #1667.
  • Add regression cases to tests/language/decorators/add-initializer.js.

[!NOTE]
Created on behalf of @frostney using Claude Opus 5.5.

Dominant language
Pascal
Stars
20
Forks
6
Avg merge
18h 43m
Merged PRs (30d)
319

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from frostney/GocciaScript

All issues in frostney/GocciaScript

Similar issues

More Compilers issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.