From af056c1676dab3b0b35666a8259db60f9bbf824e Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 24 May 2024 06:27:45 +0000 Subject: [PATCH] 8332106: VerifyError when using switch pattern in this(...) or super(...) Reviewed-by: abimpoudis, vromero --- .../classes/com/sun/tools/javac/jvm/Gen.java | 65 ++- .../com/sun/tools/javac/tree/TreeInfo.java | 10 + .../patterns/UninitializedThisException.java | 390 ++++++++++++++++++ 3 files changed, 450 insertions(+), 15 deletions(-) create mode 100644 test/langtools/tools/javac/patterns/UninitializedThisException.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index 37846b8b966..b9bfe587c6c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -172,8 +172,8 @@ public class Gen extends JCTree.Visitor { Chain switchExpressionFalseChain; List stackBeforeSwitchExpression; LocalItem switchResult; - Set invocationsWithPatternMatchingCatch = Set.of(); - ListBuffer 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 prevInvocationsWithPatternMatchingCatch = invocationsWithPatternMatchingCatch; - ListBuffer 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(), + tree.patternMatchingCatch.handler(), + code.state.dup()); internalVisitBlock(tree); } finally { + generatePatternMatchingCatch(env); + patternMatchingCatchConfiguration = prevConfiguration; + } + } + + private void generatePatternMatchingCatch(Env 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 invocations, + ListBuffer ranges, + JCCatch handler, + State startState) { + public PatternMatchingCatchConfiguration restart(State newState) { + return new PatternMatchingCatchConfiguration(invocations(), + new ListBuffer(), + handler(), + newState); + } + } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 54917c90dc2..f2edd5c97e9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -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 mapper) { diff --git a/test/langtools/tools/javac/patterns/UninitializedThisException.java b/test/langtools/tools/javac/patterns/UninitializedThisException.java new file mode 100644 index 00000000000..b0acba4e1b1 --- /dev/null +++ b/test/langtools/tools/javac/patterns/UninitializedThisException.java @@ -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 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); + } +}