8332106: VerifyError when using switch pattern in this(...) or super(...)

Reviewed-by: abimpoudis, vromero
This commit is contained in:
Jan Lahoda 2024-05-24 06:27:45 +00:00
parent da3001daf7
commit af056c1676
3 changed files with 450 additions and 15 deletions

View File

@ -172,8 +172,8 @@ public class Gen extends JCTree.Visitor {
Chain switchExpressionFalseChain;
List<LocalItem> stackBeforeSwitchExpression;
LocalItem switchResult;
Set<JCMethodInvocation> invocationsWithPatternMatchingCatch = Set.of();
ListBuffer<int[]> patternMatchingInvocationRanges;
PatternMatchingCatchConfiguration patternMatchingCatchConfiguration =
new PatternMatchingCatchConfiguration(Set.of(), null, null, null);
/** Cache the symbol to reflect the qualifying type.
* key: corresponding type
@ -1087,21 +1087,31 @@ public class Gen extends JCTree.Visitor {
}
private void visitBlockWithPatterns(JCBlock tree) {
Set<JCMethodInvocation> prevInvocationsWithPatternMatchingCatch = invocationsWithPatternMatchingCatch;
ListBuffer<int[]> prevRanges = patternMatchingInvocationRanges;
State startState = code.state.dup();
PatternMatchingCatchConfiguration prevConfiguration = patternMatchingCatchConfiguration;
try {
invocationsWithPatternMatchingCatch = tree.patternMatchingCatch.calls2Handle();
patternMatchingInvocationRanges = new ListBuffer<>();
patternMatchingCatchConfiguration =
new PatternMatchingCatchConfiguration(tree.patternMatchingCatch.calls2Handle(),
new ListBuffer<int[]>(),
tree.patternMatchingCatch.handler(),
code.state.dup());
internalVisitBlock(tree);
} finally {
generatePatternMatchingCatch(env);
patternMatchingCatchConfiguration = prevConfiguration;
}
}
private void generatePatternMatchingCatch(Env<GenContext> env) {
if (patternMatchingCatchConfiguration.handler != null &&
!patternMatchingCatchConfiguration.ranges.isEmpty()) {
Chain skipCatch = code.branch(goto_);
JCCatch handler = tree.patternMatchingCatch.handler();
code.entryPoint(startState, handler.param.sym.type);
genPatternMatchingCatch(handler, env, patternMatchingInvocationRanges.toList());
JCCatch handler = patternMatchingCatchConfiguration.handler();
code.entryPoint(patternMatchingCatchConfiguration.startState(),
handler.param.sym.type);
genPatternMatchingCatch(handler,
env,
patternMatchingCatchConfiguration.ranges.toList());
code.resolve(skipCatch);
invocationsWithPatternMatchingCatch = prevInvocationsWithPatternMatchingCatch;
patternMatchingInvocationRanges = prevRanges;
}
}
@ -1926,12 +1936,26 @@ public class Gen extends JCTree.Visitor {
if (!msym.isDynamic()) {
code.statBegin(tree.pos);
}
if (invocationsWithPatternMatchingCatch.contains(tree)) {
if (patternMatchingCatchConfiguration.invocations().contains(tree)) {
int start = code.curCP();
result = m.invoke();
patternMatchingInvocationRanges.add(new int[] {start, code.curCP()});
patternMatchingCatchConfiguration.ranges().add(new int[] {start, code.curCP()});
} else {
result = m.invoke();
if (msym.isConstructor() && TreeInfo.isConstructorCall(tree)) {
//if this is a this(...) or super(...) call, there is a pending
//"uninitialized this" before this call. One catch handler cannot
//handle exceptions that may come from places with "uninitialized this"
//and (initialized) this, hence generate one set of handlers here
//for the "uninitialized this" case, and another set of handlers
//will be generated at the end of the method for the initialized this,
//if needed:
generatePatternMatchingCatch(env);
result = m.invoke();
patternMatchingCatchConfiguration =
patternMatchingCatchConfiguration.restart(code.state.dup());
} else {
result = m.invoke();
}
}
}
@ -2555,4 +2579,15 @@ public class Gen extends JCTree.Visitor {
}
}
record PatternMatchingCatchConfiguration(Set<JCMethodInvocation> invocations,
ListBuffer<int[]> ranges,
JCCatch handler,
State startState) {
public PatternMatchingCatchConfiguration restart(State newState) {
return new PatternMatchingCatchConfiguration(invocations(),
new ListBuffer<int[]>(),
handler(),
newState);
}
}
}

View File

@ -303,6 +303,16 @@ public class TreeInfo {
}
}
/**
* Is the given method invocation an invocation of this(...) or super(...)?
*/
public static boolean isConstructorCall(JCMethodInvocation invoke) {
Name name = TreeInfo.name(invoke.meth);
Names names = name.table.names;
return (name == names._this || name == names._super);
}
/** Finds super() invocations and translates them using the given mapping.
*/
public static void mapSuperCalls(JCBlock block, Function<? super JCExpressionStatement, ? extends JCStatement> mapper) {

View File

@ -0,0 +1,390 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8332106
* @summary Verify the synthetic catch clauses are generated correctly for constructors
* @enablePreview
* @compile UninitializedThisException.java
* @run main UninitializedThisException
*/
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Objects;
import java.util.function.Supplier;
public class UninitializedThisException extends Base {
public UninitializedThisException(String s1, String s2) {
super(s1, s2);
}
public UninitializedThisException(R o1, R o2, R o3) {
out.println("-pre(" + o1.fail() + ")" +
"-nest(" + o2.fail() + ")" +
"-post(" + o3.fail() + ")");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
this(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(String o1, R o2, R o3) {
out.println("-nest(" + o2.fail() + ")" +
"-post(" + o3.fail() + ")");
String val1 = o1;
out.println("check1");
this(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(R o1, String o2, R o3) {
out.println("-pre(" + o1.fail() + ")" +
"-post(" + o3.fail() + ")");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
this(val1, o2);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(R o1, R o2, String o3) {
out.println("-pre(" + o1.fail() + ")" +
"-nest(" + o2.fail() + ")");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
this(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(R o1, String o2, String o3) {
out.println("-pre(" + o1.fail() + ")");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
this(val1, o2);
out.println("check2");
String val2 = o3;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(String o1, R o2, String o3) {
out.println("-nest(" + o2.fail() + ")");
String val1 = o1;
out.println("check1");
this(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(String o1, String o2, R o3) {
out.println("-post(" + o3.fail() + ")");
String val1 = o1;
out.println("check1");
this(val1, o2);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(R o1, R o2, R o3, boolean superMarker) {
out.println("-pre(" + o1.fail() + ")" +
"-nest(" + o2.fail() + ")" +
"-post(" + o3.fail() + ")" +
"-super");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
super(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(String o1, R o2, R o3, boolean superMarker) {
out.println("-nest(" + o2.fail() + ")" +
"-post(" + o3.fail() + ")" +
"-super");
String val1 = o1;
out.println("check1");
super(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(R o1, String o2, R o3, boolean superMarker) {
out.println("-pre(" + o1.fail() + ")" +
"-post(" + o3.fail() + ")" +
"-super");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
super(val1, o2);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(R o1, R o2, String o3, boolean superMarker) {
out.println("-pre(" + o1.fail() + ")" +
"-nest(" + o2.fail() + ")" +
"-super");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
super(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(R o1, String o2, String o3, boolean superMarker) {
out.println("-pre(" + o1.fail() + ")" +
"-super");
String val1 = o1 instanceof R(String s, _) ? s : null;
out.println("check1");
super(val1, o2);
out.println("check2");
String val2 = o3;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(String o1, R o2, String o3, boolean superMarker) {
out.println("-nest(" + o2.fail() + ")" +
"-super");
String val1 = o1;
out.println("check1");
super(val1, o2 instanceof R(String s, _) ? s : null);
out.println("check2");
String val2 = o3;
out.println("check3");
Objects.requireNonNull(val2);
}
public UninitializedThisException(String o1, String o2, R o3, boolean superMarker) {
out.println("-post(" + o3.fail() + ")" +
"-super");
String val1 = o1;
out.println("check1");
super(val1, o2);
out.println("check2");
String val2 = o3 instanceof R(String s, _) ? s : null;
out.println("check3");
Objects.requireNonNull(val2);
}
public static void main(String... args) {
runAndCatch(() -> new UninitializedThisException(new R("", true), new R("", false), new R("", false)));
runAndCatch(() -> new UninitializedThisException(new R("", false), new R("", true), new R("", false)));
runAndCatch(() -> new UninitializedThisException(new R("", false), new R("", false), new R("", true)));
new UninitializedThisException(new R("", false), new R("", false), new R("", false));
out.println();
runAndCatch(() -> new UninitializedThisException("", new R("", true), new R("", false)));
runAndCatch(() -> new UninitializedThisException("", new R("", false), new R("", true)));
new UninitializedThisException("", new R("", false), new R("", false));
out.println();
runAndCatch(() -> new UninitializedThisException(new R("", true), "", new R("", false)));
runAndCatch(() -> new UninitializedThisException(new R("", false), "", new R("", true)));
new UninitializedThisException(new R("", false), "", new R("", false));
out.println();
runAndCatch(() -> new UninitializedThisException(new R("", true), new R("", false), ""));
runAndCatch(() -> new UninitializedThisException(new R("", false), new R("", true), ""));
new UninitializedThisException(new R("", false), new R("", false), "");
out.println();
runAndCatch(() -> new UninitializedThisException(new R("", true), "", ""));
new UninitializedThisException(new R("", false), "", "");
out.println();
runAndCatch(() -> new UninitializedThisException("", new R("", true), ""));
new UninitializedThisException("", new R("", false), "");
out.println();
runAndCatch(() -> new UninitializedThisException("", "", new R("", true)));
new UninitializedThisException("", "", new R("", false));
runAndCatch(() -> new UninitializedThisException(new R("", true), new R("", false), new R("", false), true));
runAndCatch(() -> new UninitializedThisException(new R("", false), new R("", true), new R("", false), true));
runAndCatch(() -> new UninitializedThisException(new R("", false), new R("", false), new R("", true), true));
new UninitializedThisException(new R("", false), new R("", false), new R("", false), true);
out.println();
runAndCatch(() -> new UninitializedThisException("", new R("", true), new R("", false), true));
runAndCatch(() -> new UninitializedThisException("", new R("", false), new R("", true), true));
new UninitializedThisException("", new R("", false), new R("", false), true);
out.println();
runAndCatch(() -> new UninitializedThisException(new R("", true), "", new R("", false), true));
runAndCatch(() -> new UninitializedThisException(new R("", false), "", new R("", true), true));
new UninitializedThisException(new R("", false), "", new R("", false), true);
out.println();
runAndCatch(() -> new UninitializedThisException(new R("", true), new R("", false), "", true));
runAndCatch(() -> new UninitializedThisException(new R("", false), new R("", true), "", true));
new UninitializedThisException(new R("", false), new R("", false), "", true);
out.println();
runAndCatch(() -> new UninitializedThisException(new R("", true), "", "", true));
new UninitializedThisException(new R("", false), "", "", true);
out.println();
runAndCatch(() -> new UninitializedThisException("", new R("", true), "", true));
new UninitializedThisException("", new R("", false), "", true);
out.println();
runAndCatch(() -> new UninitializedThisException("", "", new R("", true), true));
new UninitializedThisException("", "", new R("", false), true);
String actualLog = log.toString().replaceAll("\\R", "\n");
String expectedLog = EXPECTED_LOG_PATTERN.replace("${super}", "") +
EXPECTED_LOG_PATTERN.replace("${super}", "-super");
if (!Objects.equals(actualLog, expectedLog)) {
throw new AssertionError("Expected log:\n" + expectedLog +
", but got: " + actualLog);
}
}
static final String EXPECTED_LOG_PATTERN =
"""
-pre(true)-nest(false)-post(false)${super}
-pre(false)-nest(true)-post(false)${super}
check1
-pre(false)-nest(false)-post(true)${super}
check1
check2
-pre(false)-nest(false)-post(false)${super}
check1
check2
check3
-nest(true)-post(false)${super}
check1
-nest(false)-post(true)${super}
check1
check2
-nest(false)-post(false)${super}
check1
check2
check3
-pre(true)-post(false)${super}
-pre(false)-post(true)${super}
check1
check2
-pre(false)-post(false)${super}
check1
check2
check3
-pre(true)-nest(false)${super}
-pre(false)-nest(true)${super}
check1
-pre(false)-nest(false)${super}
check1
check2
check3
-pre(true)${super}
-pre(false)${super}
check1
check2
check3
-nest(true)${super}
check1
-nest(false)${super}
check1
check2
check3
-post(true)${super}
check1
check2
-post(false)${super}
check1
check2
check3
""";
static final StringWriter log = new StringWriter();
static final PrintWriter out = new PrintWriter(log);
static void runAndCatch(Supplier<Object> toRun) {
try {
toRun.get();
throw new AssertionError("Didn't get the expected exception!");
} catch (MatchException ex) {
//OK
}
}
record R(String s, boolean fail) {
public String s() {
if (fail) {
throw new NullPointerException();
} else {
return s;
}
}
}
}
class Base {
public Base(String s1, String s2) {
Objects.requireNonNull(s1);
Objects.requireNonNull(s2);
}
}