[dart2js] Fix labeled jump target resolution in SSA builder

When generating SSA jump instructions (HContinue and HBreak) for AST break statements, ensure that we only select labels from the target's label list that are explicitly marked as valid continue (isContinueTarget) or break (isBreakTarget) targets.

Previously, handler.labels.first was selected indiscriminately if non-empty, which caused unlabeled continue statements inside loops that had an outer break label to incorrectly generate labeled continue jumps targeting the break label.

Fixes: https://github.com/dart-lang/sdk/issues/63456
Change-Id: Ic497776141a192edb0930f4585cdaae2feecb3d5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/510280
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
Nate Biggs
2026-06-09 19:23:06 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 31b6c9f0d2
commit 6282b35c4c
2 changed files with 57 additions and 4 deletions
+18 -4
View File
@@ -3457,14 +3457,28 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
JumpHandler handler = jumpTargets[target]!;
final sourceInformation = _sourceInformationBuilder.buildGoto(node);
if (_localsMap.generateContinueForBreak(node)) {
if (handler.labels.isNotEmpty) {
handler.generateContinue(sourceInformation, handler.labels.first);
LabelDefinition? continueLabel;
for (final label in handler.labels) {
if (label.isContinueTarget) {
continueLabel = label;
break;
}
}
if (continueLabel != null) {
handler.generateContinue(sourceInformation, continueLabel);
} else {
handler.generateContinue(sourceInformation);
}
} else {
if (handler.labels.isNotEmpty) {
handler.generateBreak(sourceInformation, handler.labels.first);
LabelDefinition? breakLabel;
for (final label in handler.labels) {
if (label.isBreakTarget) {
breakLabel = label;
break;
}
}
if (breakLabel != null) {
handler.generateBreak(sourceInformation, breakLabel);
} else {
handler.generateBreak(sourceInformation);
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:expect/expect.dart';
@pragma('dart2js:never-inline')
List<List<int>> getPolys() => [
[1, 3],
[3, 4],
[5],
];
@pragma('dart2js:prefer-inline')
int nextP(int p) {
if (p < 0) print(p);
return p + 1;
}
void main() {
var polys = getPolys();
int count = 0;
for (int iter = 0; iter < 2; iter++) {
bool removedAny = false;
outer:
for (int p = 0; p < polys.length; p = nextP(p)) {
final poly = polys[p];
if (poly.length <= 1) continue outer;
for (int i = 0; i < poly.length; i++) {
if (poly[i] == 2) {
removedAny = true;
break outer;
}
}
}
count++;
}
Expect.equals(2, count);
}