Skip to content

fix(compile): initialize namespace fields in the multi-module entry point (#1245)#1247

Merged
nickna merged 1 commit into
mainfrom
worktree-issue-1245
Jul 4, 2026
Merged

fix(compile): initialize namespace fields in the multi-module entry point (#1245)#1247
nickna merged 1 commit into
mainfrom
worktree-issue-1245

Conversation

@nickna

@nickna nickna commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #1245. In compiled mode, any program containing both a namespace declaration and a top-level import (which makes the file an ES module) crashed at startup with a NullReferenceException in $TSNamespace.Set, called from the module's $Initialize. The interpreter ran the same program correctly.

Root cause

Namespace objects live in flat static fields ($ns_*) on $Program, shared across every module. A module's $Initialize body only populates them — EmitNamespace emits $ns_X.Set(...) — it never creates them.

  • The single-file entry point (EmitSingleFileEntryPoint / EmitExeEntryPointWithUserMain) calls InitializeNamespaceFields(il) at the top of Main, before any top-level code runs.
  • The multi-module entry point (EmitModulesEntryPoint, used whenever the program has imports) omitted that call.

So when a module's $Initialize ran $ns_NS.Set("x", 1), $ns_NS was still null → NRE. A namespace in a plain script (no import) was fine because that path uses the single-file entry point.

Fix

Call InitializeNamespaceFields(il) in EmitModulesEntryPoint before the module $Initialize dispatch loop, mirroring the single-file path. Namespace fields are global/flat and defined once, so initializing them all in Main before any module init covers ES-module, script, and lazily-required CommonJS modules alike.

Tests

New NamespaceInModuleTests (runs both interpreter and compiled via ExecutionModes.All):

  • namespace + import in the entry file — exported const, import after (the exact issue repro)
  • exported function member, import before the namespace
  • nested namespaces
  • namespace alongside a sibling-module import, with class and enum members

Confirmed the 4 compiled-mode tests fail with NullReferenceException without the fix and pass with it (interpreted always passed).

Verification

…oint (#1245)

A `namespace` declaration combined with any top-level `import` (which turns the
file into an ES module) crashed compiled programs at startup with a
NullReferenceException in `$TSNamespace.Set`, called from a module's
`$Initialize`. The interpreter ran the same program correctly.

Namespace objects live in flat static fields (`$ns_*`) on `$Program`, shared
across every module. A module's `$Initialize` body only POPULATES them
(`EmitNamespace` emits `$ns_X.Set(...)`); it never creates them. The single-file
entry point calls `InitializeNamespaceFields` at the top of `Main` before any
top-level code runs, but the multi-module entry point (`EmitModulesEntryPoint`)
omitted it — so the first `Set()` dereferenced a null namespace field.

Fix: call `InitializeNamespaceFields(il)` in `EmitModulesEntryPoint` before the
module `$Initialize` dispatch loop, mirroring the single-file path.

Adds regression tests (both interpreter and compiled) covering namespace +
import in the entry file (const/function members, import before/after),
nested namespaces, and a namespace alongside a sibling-module import with
class/enum members.
@nickna nickna merged commit 35b62b1 into main Jul 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compiled: a namespace declaration in an ES-module context (any import present) crashes at startup with NullReferenceException in $TSNamespace.Set

1 participant