ReviewOS

home-lang/home

Native module loader: require() a linked ESM record without top-level await

#620
Closed glennmichael123 opened this 10 days ago ·
10 days ago

Problem

The complete unchanged upstream js/node/http2/node-http2.test.js never reaches its first test in Home. The pinned Bun control runs it fully.

runtimeresult
Home (zig build debug -Denable_jsc=true, source pin 4982b91e3702094330f3be3883354c52b8c01323)0 pass / 1 fail / 1 error
Bun 1.4.0-canary.1+4982b91e3 (built from the same pin)287 pass / 6 skip / 0 fail / 12,192 assertions

Home aborts while loading the file's dependencies:

TypeError: require() async module ".../packages/runtime/test/bun-corpus/harness.ts" is unsupported. use "await import()" instead.
      at <anonymous> (.../js/node/http2/http2-helpers.cjs:3:7)

harness.ts has no top-level await. The load shape is: the ESM test statically imports "harness" and "./http2-helpers", and that CommonJS helper then calls require("harness") on the module the ESM graph has already linked. Home classifies that record as an async module and refuses the synchronous require; Bun resolves it.

Reproduction

HOME_NATIVE_VM=1 HOME_CORPUS_FULL_VM=1 ./zig-out/bin/home-debug test \
  packages/runtime/test/bun-corpus/js/node/http2/node-http2.test.js

Control: ~/Code/bun/build/release/bun test <same path>.

What is NOT the mechanism

Three narrower shapes all behave identically in Home and Bun, so none of them isolates it:

  • require() of a plain .ts ESM module from a .cjs, by relative path — passes in both.
  • The same through the corpus's tsconfig paths alias (require("harness") from a .cjs at the corpus root) — passes in both.
  • An ESM entry that imports a module and a .cjs that requires that same module — fails in Bun too, so Home's rejection there is correct behaviour, not the gap.

Something about the real file's graph makes Bun's loader treat the record as synchronously requirable where the reduced case does not. That difference is the thing to find; the reduction above is recorded so the next person does not repeat it.

Acceptance criteria

  • The complete unchanged js/node/http2/node-http2.test.js reaches and runs its tests, matching the pinned Bun control.
  • No corpus file, fixture, or helper is edited, and no assertion, workload, deadline, or skip is added.
  • The fix is the pinned loader's actual rule for when a linked record may be required, not a special case for this file, this helper, or harness.ts.
  • Adjacent js/node/http2 files stay green: the directory is currently 4 pass / 1 fail / 1 crash / 0 hang, with this file the crash and h2-conformance.test.ts the separate failure.

Parent parity tracker: #66

10 days ago

Resolved in https://github.com/home-lang/home/commit/25feaf2f4690bbb4b82a1cd5e4e8cabddcf66d11.

Corrected root cause: this was not a missing native module-loader rule. The copied corpus was invoked from the Home repository root, so Bun configuration discovery selected Home's root bunfig.toml and omitted the unchanged corpus preload.ts. The original Bun checkout passed because its root bunfig preloads test/preload.ts; the same pinned Bun binary also passes the copied file when run from the mirrored corpus root and fails it when run from the Home root.

The full-VM dispatcher now changes to the mirrored corpus root and rewrites only corpus target arguments before handing control to TestCommand. That preserves upstream preload discovery, tsconfig paths, and fixture-relative behavior for root, directory, single-file, and multi-file invocations. No module-loader behavior or corpus source file was changed.

Verification:

  • Debug full VM, node-http2.test.js: 287 pass / 6 upstream memory-leak skips / 0 fail / 12,192 assertions.
  • Debug full VM, five HTTP/2 files: 329 pass / 6 upstream skips / 0 fail / 12,246 assertions.
  • ReleaseFast full VM, same five files: 329 pass / 6 upstream skips / 0 fail / 12,246 assertions.
  • Adjacent require/ESM race, TLA, microtask-order, GC-root, and registry-lifetime batch: 6 pass / 0 fail.

The stale node-http2.test.js failure expectation is removed.

Sign in to comment on this issue.