ReviewOS

home-lang/home

assert.deepStrictEqual prints an unbounded diff for large arrays

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

Problem

assert.deepStrictEqual on two 20,000-element arrays with 500 differing entries produces a 4,504-line message in Home and a 55-line one in pinned Bun. The unchanged upstream js/node/assert/assert-typedarray-deepequal.test.ts asserts the message stays under 100 lines and does not mention tail elements, so Home fails it 44 pass / 1 fail against Bun's 45 / 0.

What each runtime does

internal/assert/assertion_error.ts has two paths. The normal one runs myersDiff and prints a real diff; when myersDiff throws, the catch falls back to a 50-line preview with an empty header and skipped = true.

The message shapes show they take different branches:

line 2line 3linescontains 19960
Bun""... Skipped lines55no
Home+ actual - expected... Skipped lines4504yes

Bun's empty header is the fallback branch — its myersDiff throws. Home's completes and prints the full diff.

This is not a source divergence

Both runtimes execute the same JavaScript. build-support/bundle-native-modules.ts carries an explicit ownership manifest:

const ownedModules = ['node/url.ts', 'node/worker_threads.ts']

Everything else is served verbatim from the linked build's InternalModuleRegistryConstants.h, so internal/assert/assertion_error and internal/assert/myers_diff are Bun's own bytes in both. (The mirrored copies under packages/runtime/upstream/src/js/ are reference source and are not compiled — editing them changes nothing. I confirmed that by patching assertion_error.ts, rebuilding, and measuring no behavioural change; the edit was reverted.)

So the difference is in the engine or in what the diff is handed, not in the port:

  • myersDiff throwing in Bun is most likely an allocation or recursion limit reached on a 20,000-line input. Whatever raises it in Bun does not fire in Home, so Home completes an O(ND) diff that upstream treats as unbounded.
  • The other candidate is util.inspect handing the two runtimes differently shaped input.

Worth settling which before changing anything: if Home simply has a higher effective limit, matching upstream by lowering a limit would be the wrong fix, and the right one is an explicit size guard in the diff path.

Reproduction

// adiff.mjs
import assert from "node:assert";
const length = 20000;
const actual = Array.from({ length }, (_, i) => i);
const expected = actual.slice();
for (let i = 0; i < length; i += 40) expected[i] = -1;
try { assert.deepStrictEqual(actual, expected); } catch (e) {
  console.log(e.message.split("\n").length, e.message.includes("19960"));
}

Home prints 4504 true; ~/Code/bun/build/release/bun run adiff.mjs prints 55 false.

Acceptance criteria

  • The unchanged upstream file passes 45 / 0, matching the pinned Bun control.
  • The bound comes from the same mechanism upstream uses, not a special case for typed arrays or for this input size.
  • No corpus file, fixture, assertion or workload is edited.

Broader note

The two-module ownership manifest means JS-level builtin fixes generally cannot be made in Home's tree today — a gap whose fix lives in internal/* or most of node/* needs that module added to ownedModules (and its ABI validated) before any source change has effect. That is worth knowing before picking up any of the remaining js/node failures in #621.

Parent parity tracker: #66

10 days ago

Fixed and pushed in 7b79c9b.

The native Myers implementation now has a deterministic 64 MiB edit-trace budget, matching the exact pinned-Bun boundary instead of depending on allocator growth behavior. The unchanged upstream js/node/assert/assert-typedarray-deepequal.test.ts now passes 45/45. A boundary probe matches pinned Bun exactly: 104 spaced replacements produce the full 940-line diff, while 105 produces the bounded 55-line fallback.

Also added a unit regression for the trace budget. No corpus file, fixture, assertion, deadline, or workload changed. Parent: #66.

Sign in to comment on this issue.