Conversation
An unlabeled `break` inside a labeled switch (`outer: switch (x) { case 1:
foo(); break; }`) was not absorbed by the switch. JintSwitchStatement's
HandleCompletion only converted a Break completion to Normal when the break's
target equalled the switch's own label:
if (r.Type == CompletionType.Break
&& string.Equals(context.Target, _statement.LabelSet?.Name, ...))
For an unlabeled break context.Target is null. When the switch is unlabeled
LabelSet?.Name is also null, so null == null absorbed it. But once the switch
carries a label, LabelSet.Name is non-null, so the unlabeled break (target
null) failed the equality check and propagated out as a Break completion —
skipping any statements after the switch and, at function scope, causing the
body to complete abnormally so the function returned undefined.
All loop statements (for, for-in/of, while, do-while) already guard this with
`context.Target == null || string.Equals(...)`; the switch was the only
breakable missing the null-target arm. Add it so a switch always absorbs an
unlabeled break regardless of its own label, matching the loops and the spec
(a SwitchStatement absorbs break completions with an empty target).
This surfaced running minified React 19 in a JS DOM: React's
createFiberFromTypeAndProps compiles to `e: switch (e) { case T: o = 8;
l |= 24; break; ... }` and returned undefined for StrictMode elements, which
then blew up downstream as "Cannot convert undefined or null to object".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lahma
enabled auto-merge (squash)
July 9, 2026 09:32
This was referenced Jul 13, 2026
This was referenced Jul 20, 2026
This was referenced Jul 28, 2026
This was referenced Jul 28, 2026
This was referenced Aug 5, 2026
This was referenced Aug 14, 2026
This was referenced Aug 24, 2026
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Absorb an unlabeled
breakinside a labeledswitchstatement.Details
An unlabeled
breakinside a labeledswitchwas not absorbed by the switch:JintSwitchStatement.HandleCompletiononly converted aBreakcompletion toNormalwhen the break's target equalled the switch's own label:For an unlabeled
break,context.Targetisnull. When the switch is unlabeled,LabelSet?.Nameis alsonull, sonull == nullabsorbed it. But once the switch carries a label,LabelSet.Nameis non-null, so the unlabeled break (targetnull) failed the equality check and propagated out as aBreakcompletion — skipping statements after the switch and, at function scope, making the body complete abnormally so the function returnedundefined.All loop statements (
for,for-in/of,while,do-while) already guard this withcontext.Target == null || string.Equals(...); the switch was the only breakable missing the null-target arm. This adds it, so a switch always absorbs an unlabeled break regardless of its own label — matching the loops and the spec (aSwitchStatementabsorbs break completions with an empty target).Found running minified React 19 inside a pure-JS DOM:
createFiberFromTypeAndPropscompiles toe: switch (e) { case T: o = 8; l |= 24; break; ... }and returnedundefinedforStrictModeelements, which then blew up downstream asCannot convert undefined or null to object.Linked issue
No tracking issue — discovered while running minified React 19 on Jint. Refs #
Test plan
Jint.Testsdotnet test --configuration ReleaselocallyJint.Tests.Test262and confirmed no regressionsJint.Tests/Runtime/InteropJint.BenchmarkAdded three regression tests to
Jint.Tests/Runtime/SwitchTests.cs(unlabeled break absorbed by a labeled switch; labeled break still escapes correctly; unlabeled break in a nested switch stays innermost). 3168 unit tests pass, and 737 Test262switch/labeled/break/continueconformance tests pass, 0 failures.Breaking change?
No.