Skip to content

Commit a15db1a

Browse files
committed
8302865: Illegal bytecode for break from if with instanceof pattern matching condition
Reviewed-by: vromero
1 parent 67fbd87 commit a15db1a

File tree

6 files changed

+195
-68
lines changed

6 files changed

+195
-68
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,12 +1447,7 @@ public void visitBlock(JCBlock tree) {
14471447
public void visitDoLoop(JCDoWhileLoop tree) {
14481448
attribStat(tree.body, env.dup(tree));
14491449
attribExpr(tree.cond, env, syms.booleanType);
1450-
if (!breaksOutOf(tree, tree.body)) {
1451-
//include condition's body when false after the while, if cannot get out of the loop
1452-
MatchBindings condBindings = matchBindings;
1453-
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
1454-
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
1455-
}
1450+
handleLoopConditionBindings(matchBindings, tree, tree.body);
14561451
result = null;
14571452
}
14581453

@@ -1466,19 +1461,10 @@ public void visitWhileLoop(JCWhileLoop tree) {
14661461
} finally {
14671462
whileEnv.info.scope.leave();
14681463
}
1469-
if (!breaksOutOf(tree, tree.body)) {
1470-
//include condition's bindings when false after the while, if cannot get out of the loop
1471-
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
1472-
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
1473-
}
1464+
handleLoopConditionBindings(condBindings, tree, tree.body);
14741465
result = null;
14751466
}
14761467

1477-
private boolean breaksOutOf(JCTree loop, JCTree body) {
1478-
preFlow(body);
1479-
return flow.breaksOutOf(env, loop, body, make);
1480-
}
1481-
14821468
public void visitForLoop(JCForLoop tree) {
14831469
Env<AttrContext> loopEnv =
14841470
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
@@ -1503,11 +1489,50 @@ public void visitForLoop(JCForLoop tree) {
15031489
finally {
15041490
loopEnv.info.scope.leave();
15051491
}
1506-
if (!breaksOutOf(tree, tree.body)) {
1507-
//include condition's body when false after the while, if cannot get out of the loop
1508-
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
1509-
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
1492+
handleLoopConditionBindings(condBindings, tree, tree.body);
1493+
}
1494+
1495+
/**
1496+
* Include condition's bindings when false after the loop, if cannot get out of the loop
1497+
*/
1498+
private void handleLoopConditionBindings(MatchBindings condBindings,
1499+
JCStatement loop,
1500+
JCStatement loopBody) {
1501+
if (condBindings.bindingsWhenFalse.nonEmpty() &&
1502+
!breaksTo(env, loop, loopBody)) {
1503+
addBindings2Scope(loop, condBindings.bindingsWhenFalse);
1504+
}
1505+
}
1506+
1507+
private boolean breaksTo(Env<AttrContext> env, JCTree loop, JCTree body) {
1508+
preFlow(body);
1509+
return flow.breaksToTree(env, loop, body, make);
1510+
}
1511+
1512+
/**
1513+
* Add given bindings to the current scope, unless there's a break to
1514+
* an immediately enclosing labeled statement.
1515+
*/
1516+
private void addBindings2Scope(JCStatement introducingStatement,
1517+
List<BindingSymbol> bindings) {
1518+
if (bindings.isEmpty()) {
1519+
return ;
1520+
}
1521+
1522+
var searchEnv = env;
1523+
while (searchEnv.tree instanceof JCLabeledStatement labeled &&
1524+
labeled.body == introducingStatement) {
1525+
if (breaksTo(env, labeled, labeled.body)) {
1526+
//breaking to an immediately enclosing labeled statement
1527+
return ;
1528+
}
1529+
searchEnv = searchEnv.next;
1530+
introducingStatement = labeled;
15101531
}
1532+
1533+
//include condition's body when false after the while, if cannot get out of the loop
1534+
bindings.forEach(env.info.scope::enter);
1535+
bindings.forEach(BindingSymbol::preserveBinding);
15111536
}
15121537

15131538
public void visitForeachLoop(JCEnhancedForLoop tree) {
@@ -2242,8 +2267,7 @@ public void visitIf(JCIf tree) {
22422267
afterIfBindings = condBindings.bindingsWhenFalse;
22432268
}
22442269

2245-
afterIfBindings.forEach(env.info.scope::enter);
2246-
afterIfBindings.forEach(BindingSymbol::preserveBinding);
2270+
addBindings2Scope(tree, afterIfBindings);
22472271

22482272
result = null;
22492273
}

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -287,18 +287,18 @@ public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) {
287287
}
288288
}
289289

290-
public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) {
290+
public boolean breaksToTree(Env<AttrContext> env, JCTree breakTo, JCTree body, TreeMaker make) {
291291
//we need to disable diagnostics temporarily; the problem is that if
292292
//"that" contains e.g. an unreachable statement, an error
293293
//message will be reported and will cause compilation to skip the flow analysis
294294
//step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
295295
//related errors, which will allow for more errors to be detected
296296
Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
297297
try {
298-
SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer();
298+
SnippetBreakToAnalyzer analyzer = new SnippetBreakToAnalyzer(breakTo);
299299

300300
analyzer.analyzeTree(env, body, make);
301-
return analyzer.breaksOut();
301+
return analyzer.breaksTo();
302302
} finally {
303303
log.popDiagnosticHandler(diagHandler);
304304
}
@@ -1909,52 +1909,21 @@ public boolean isAlive() {
19091909
}
19101910
}
19111911

1912-
class SnippetBreakAnalyzer extends AliveAnalyzer {
1913-
private final Set<JCTree> seenTrees = new HashSet<>();
1914-
private boolean breaksOut;
1912+
class SnippetBreakToAnalyzer extends AliveAnalyzer {
1913+
private final JCTree breakTo;
1914+
private boolean breaksTo;
19151915

1916-
public SnippetBreakAnalyzer() {
1917-
}
1918-
1919-
@Override
1920-
public void visitLabelled(JCTree.JCLabeledStatement tree) {
1921-
seenTrees.add(tree);
1922-
super.visitLabelled(tree);
1923-
}
1924-
1925-
@Override
1926-
public void visitWhileLoop(JCTree.JCWhileLoop tree) {
1927-
seenTrees.add(tree);
1928-
super.visitWhileLoop(tree);
1929-
}
1930-
1931-
@Override
1932-
public void visitForLoop(JCTree.JCForLoop tree) {
1933-
seenTrees.add(tree);
1934-
super.visitForLoop(tree);
1935-
}
1936-
1937-
@Override
1938-
public void visitForeachLoop(JCTree.JCEnhancedForLoop tree) {
1939-
seenTrees.add(tree);
1940-
super.visitForeachLoop(tree);
1941-
}
1942-
1943-
@Override
1944-
public void visitDoLoop(JCTree.JCDoWhileLoop tree) {
1945-
seenTrees.add(tree);
1946-
super.visitDoLoop(tree);
1916+
public SnippetBreakToAnalyzer(JCTree breakTo) {
1917+
this.breakTo = breakTo;
19471918
}
19481919

19491920
@Override
19501921
public void visitBreak(JCBreak tree) {
1951-
breaksOut |= (super.alive == Liveness.ALIVE &&
1952-
!seenTrees.contains(tree.target));
1953-
super.visitBreak(tree);
1922+
breaksTo |= breakTo == tree.target && super.alive == Liveness.ALIVE;
19541923
}
19551924

1956-
public boolean breaksOut() {
1957-
return breaksOut;
1925+
public boolean breaksTo() {
1926+
return breaksTo;
19581927
}
19591928
}
19601929

test/langtools/tools/javac/patterns/BindingsTest1.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,67 @@ public void run() {
231231
throw new AssertionError();
232232
}
233233

234+
{
235+
L: {
236+
while (!(o1 instanceof String s)) {
237+
break L;
238+
}
239+
240+
s.length();
241+
}
242+
}
243+
244+
{
245+
L: {
246+
for (; !(o1 instanceof String s); ) {
247+
break L;
248+
}
249+
250+
s.length();
251+
}
252+
}
253+
254+
{
255+
int j = 0;
256+
L: while (j++ < 2)
257+
if (!(o1 instanceof String s)) {
258+
break L;
259+
}
260+
}
261+
262+
{
263+
int j = 0;
264+
L: for (; j++ < 2; )
265+
if (!(o1 instanceof String s)) {
266+
break L;
267+
}
268+
}
269+
270+
{
271+
//"s" in the outter scope does not flow out of the if, but
272+
//variables inside a lambda or anonymous or local class may:
273+
L: if (!(o1 instanceof String s)) {
274+
Runnable r = () -> {
275+
NESTED: {
276+
if (!(o1 instanceof String n)) {
277+
break NESTED;
278+
}
279+
280+
n.length();
281+
}
282+
};
283+
break L;
284+
}
285+
}
286+
287+
switch (0) {
288+
case 0:
289+
if (!(o1 instanceof String s)) {
290+
break;
291+
}
292+
s.length();
293+
}
294+
234295
//binding in an anonymous class:
235296
if (!(invokeOnce("") instanceof String s)) {
236297
throw new AssertionError();

test/langtools/tools/javac/patterns/BindingsTest2.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,5 +247,62 @@ public static void meth() {
247247
s = "";
248248
}
249249
}
250+
{
251+
LBL1: LBL2: if (!(o1 instanceof String s)) {
252+
break LBL1;
253+
}
254+
255+
System.err.println(s);
256+
}
257+
{
258+
LBL1: LBL2: if (!(o1 instanceof String s)) {
259+
break LBL2;
260+
}
261+
262+
System.err.println(s);
263+
}
264+
{
265+
LBL1: LBL2: if (o1 instanceof String s) {
266+
} else {
267+
break LBL1;
268+
}
269+
270+
System.err.println(s);
271+
}
272+
{
273+
LBL1: LBL2: if (o1 instanceof String s) {
274+
} else {
275+
break LBL2;
276+
}
277+
278+
System.err.println(s);
279+
}
280+
{
281+
switch (0) {
282+
case 0:
283+
if (!(o1 instanceof String s)) {
284+
break;
285+
}
286+
}
287+
s.length();
288+
}
289+
290+
{
291+
int j = 0;
292+
L: while (j++ < 2)
293+
if (!(o1 instanceof String s)) {
294+
break L;
295+
}
296+
s.length();
297+
}
298+
299+
{
300+
int j = 0;
301+
L: for (; j++ < 2; )
302+
if (!(o1 instanceof String s)) {
303+
break L;
304+
}
305+
s.length();
306+
}
250307
}
251308
}

test/langtools/tools/javac/patterns/BindingsTest2.out

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,16 @@ BindingsTest2.java:179:13: compiler.err.cant.resolve.location: kindname.variable
4343
BindingsTest2.java:196:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
4444
BindingsTest2.java:204:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
4545
BindingsTest2.java:212:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
46-
BindingsTest2.java:221:17: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
47-
BindingsTest2.java:231:17: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
48-
BindingsTest2.java:241:17: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
4946
BindingsTest2.java:247:17: compiler.err.cant.assign.val.to.var: final, s
47+
BindingsTest2.java:255:32: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
48+
BindingsTest2.java:262:32: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
49+
BindingsTest2.java:270:32: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
50+
BindingsTest2.java:278:32: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
51+
BindingsTest2.java:287:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
52+
BindingsTest2.java:296:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
53+
BindingsTest2.java:305:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
5054
BindingsTest2.java:135:17: compiler.err.unreachable.stmt
5155
BindingsTest2.java:160:17: compiler.err.unreachable.stmt
5256
BindingsTest2.java:185:17: compiler.err.unreachable.stmt
53-
52 errors
57+
BindingsTest2.java:241:17: compiler.err.unreachable.stmt
58+
57 errors

test/langtools/tools/javac/patterns/BreakAndLoops.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ protected void doWork() throws Throwable {
104104
shouldPass = true;
105105
} else if (innerLoop.supportsAnonymousBreak && brk == Break.BREAK) {
106106
shouldPass = true;
107+
} else if (outterLabel == OutterLabel.LABEL && brk == Break.BREAK_LABEL && outterLoop != OutterLoop.NONE) {
108+
shouldPass = switch(mainLoop) {
109+
case WHILE, FOR -> true;
110+
case DO_WHILE -> switch (innerLoop) {
111+
case WHILE, FOR, FOR_EACH -> true;
112+
//the statement following the do-while is unreachable:
113+
case BLOCK, DO_WHILE, NONE -> {
114+
yield false;
115+
}
116+
};
117+
};
107118
} else {
108119
shouldPass = false;
109120
}

0 commit comments

Comments
 (0)