8332106: VerifyError when using switch pattern in this(...) or super(...)
Reviewed-by: abimpoudis, vromero
This commit is contained in:
parent
da3001daf7
commit
af056c1676
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user