Skip to content

fix(runtime): implement assert throws helpers - #1924

Merged
proggeramlug merged 1 commit into
PerryTS:mainfrom
andrewtdiz:codex/node-compat-assert-throws-sync
May 27, 2026
Merged

proggeramlug merged 1 commit into
PerryTS:mainfrom
andrewtdiz:codex/node-compat-assert-throws-sync

Conversation

@andrewtdiz

@andrewtdiz andrewtdiz commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • implement synchronous assert.throws and assert.doesNotThrow for node:assert and node:assert/strict
  • trap callback exceptions through Perry's native exception stack
  • support regexp, built-in Error constructor, and shallow error-object matcher cases
  • wire static/native lowering, callable exports, API manifest/docs, and runtime tests

Verification

  • ./run_parity_tests.sh --suite node-suite --module assert --filter generated-message/throws-generated PASS
  • ./run_parity_tests.sh --suite node-suite --module assert --filter errors/throws PASS 2/2
  • ./run_parity_tests.sh --suite node-suite --module assert --filter errors/strict-throws-validation PASS
  • ./run_parity_tests.sh --suite node-suite --module assert PASS threshold: 55 pass / 8 fail / 0 compile fail
  • cargo test -p perry-runtime assert --lib PASS
  • cargo test -p perry-codegen manifest_consistency --lib PASS
  • bash scripts/regen_api_docs.sh PASS
  • cargo fmt --all -- --check PASS
  • git diff --check PASS
  • jq empty test-parity/known_failures.json PASS

Residual full-suite assert failures are unrelated lanes: deep equality edge cases, async rejects, and CallTracker version skew. Full dynamic Error property storage is also left out of this slice.

@andrewtdiz
andrewtdiz force-pushed the codex/node-compat-assert-throws-sync branch 3 times, most recently from f562ee6 to e8c8d12 Compare May 27, 2026 05:38
@andrewtdiz
andrewtdiz force-pushed the codex/node-compat-assert-throws-sync branch from e8c8d12 to 63b983c Compare May 27, 2026 05:42
@proggeramlug
proggeramlug merged commit 6adb51a into PerryTS:main May 27, 2026
10 checks passed
TheHypnoo added a commit that referenced this pull request Aug 31, 2026
…t terminal

The previous commit on this branch added `return false` after the RegExp
block. That stopped the fallthrough into `instanceof`, but kept the line that
caused it: a retry of the pattern against `thrown.message`.

Node tests a RegExp matcher against `String(thrown)` and nothing else, and
treats RegExp as a terminal matcher category. The whole block collapses to one
statement, which fixes both halves at the single point `throws`,
`doesNotThrow`, `rejects`, and `doesNotReject` all route through:

- An anchored pattern matching the bare message but not the stringified error
  was wrongly accepted. `assert.throws(() => { throw new Error("nope") },
  /^nope/)` passed, where Node reports an AssertionError because `String(err)`
  is "Error: nope". Same for a thrown non-error carrying a `message` property,
  whose `String()` is "[object Object]".
- A pattern matching neither reached `js_instanceof_dynamic(thrown, regexp)`
  and surfaced as a TypeError instead of an AssertionError, so throws/rejects
  reported the wrong error class and doesNotThrow/doesNotReject failed to
  rethrow the original error.

A RegExp value on a validator key (`{ message: /bad/ }`) is a different matcher
and is still tested against that property in `object_matcher_matches`.

The `thrown.message` retry dates to the original implementation in #1924 and
was never a workaround for anything else.

The pre-existing `assert/errors/strict-throws-validation.ts` could not catch
either bug: `/will-not-match/` matches neither input, so it cannot tell them
apart, and it prints only `name` and `code`. The new fixture covers both inputs
across all four entry points and matches Node 26.5.1 byte for byte. The unit
tests replace the setjmp-based one, which the previous fix also passed.

Fixes #9227
Refs #9202

Claude-Session: https://claude.ai/code/session_01QFubuLjMeJBhrHSfCuoPMp
proggeramlug pushed a commit that referenced this pull request Aug 31, 2026
…t terminal

The previous commit on this branch added `return false` after the RegExp
block. That stopped the fallthrough into `instanceof`, but kept the line that
caused it: a retry of the pattern against `thrown.message`.

Node tests a RegExp matcher against `String(thrown)` and nothing else, and
treats RegExp as a terminal matcher category. The whole block collapses to one
statement, which fixes both halves at the single point `throws`,
`doesNotThrow`, `rejects`, and `doesNotReject` all route through:

- An anchored pattern matching the bare message but not the stringified error
  was wrongly accepted. `assert.throws(() => { throw new Error("nope") },
  /^nope/)` passed, where Node reports an AssertionError because `String(err)`
  is "Error: nope". Same for a thrown non-error carrying a `message` property,
  whose `String()` is "[object Object]".
- A pattern matching neither reached `js_instanceof_dynamic(thrown, regexp)`
  and surfaced as a TypeError instead of an AssertionError, so throws/rejects
  reported the wrong error class and doesNotThrow/doesNotReject failed to
  rethrow the original error.

A RegExp value on a validator key (`{ message: /bad/ }`) is a different matcher
and is still tested against that property in `object_matcher_matches`.

The `thrown.message` retry dates to the original implementation in #1924 and
was never a workaround for anything else.

The pre-existing `assert/errors/strict-throws-validation.ts` could not catch
either bug: `/will-not-match/` matches neither input, so it cannot tell them
apart, and it prints only `name` and `code`. The new fixture covers both inputs
across all four entry points and matches Node 26.5.1 byte for byte. The unit
tests replace the setjmp-based one, which the previous fix also passed.

Fixes #9227
Refs #9202

Claude-Session: https://claude.ai/code/session_01QFubuLjMeJBhrHSfCuoPMp
proggeramlug pushed a commit that referenced this pull request Aug 31, 2026
…t terminal (#9228)

* fix(assert): stop regexp matcher fallthrough

* fix(assert): test a RegExp matcher against String(thrown), and make it terminal

The previous commit on this branch added `return false` after the RegExp
block. That stopped the fallthrough into `instanceof`, but kept the line that
caused it: a retry of the pattern against `thrown.message`.

Node tests a RegExp matcher against `String(thrown)` and nothing else, and
treats RegExp as a terminal matcher category. The whole block collapses to one
statement, which fixes both halves at the single point `throws`,
`doesNotThrow`, `rejects`, and `doesNotReject` all route through:

- An anchored pattern matching the bare message but not the stringified error
  was wrongly accepted. `assert.throws(() => { throw new Error("nope") },
  /^nope/)` passed, where Node reports an AssertionError because `String(err)`
  is "Error: nope". Same for a thrown non-error carrying a `message` property,
  whose `String()` is "[object Object]".
- A pattern matching neither reached `js_instanceof_dynamic(thrown, regexp)`
  and surfaced as a TypeError instead of an AssertionError, so throws/rejects
  reported the wrong error class and doesNotThrow/doesNotReject failed to
  rethrow the original error.

A RegExp value on a validator key (`{ message: /bad/ }`) is a different matcher
and is still tested against that property in `object_matcher_matches`.

The `thrown.message` retry dates to the original implementation in #1924 and
was never a workaround for anything else.

The pre-existing `assert/errors/strict-throws-validation.ts` could not catch
either bug: `/will-not-match/` matches neither input, so it cannot tell them
apart, and it prints only `name` and `code`. The new fixture covers both inputs
across all four entry points and matches Node 26.5.1 byte for byte. The unit
tests replace the setjmp-based one, which the previous fix also passed.

Fixes #9227
Refs #9202

Claude-Session: https://claude.ai/code/session_01QFubuLjMeJBhrHSfCuoPMp
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.

2 participants