diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 29a9bf3c8cb..8ea78a8cc78 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -258,12 +258,14 @@ public class TransPatterns extends TreeTranslator { extraConditions = translate(extraConditions); resultExpression = makeBinary(Tag.AND, resultExpression, extraConditions); } - if (currentValue != exprSym) { - resultExpression = - make.at(tree.pos).LetExpr(make.VarDef(currentValue, translatedExpr), - resultExpression).setType(syms.booleanType); - ((LetExpr) resultExpression).needsCond = true; - } + List tempVars = currentValue != exprSym + ? List.of(make.VarDef(currentValue, translatedExpr)) + : List.nil(); + resultExpression = + make.at(tree.pos).LetExpr(tempVars, + resultExpression).setType(syms.booleanType); + ((LetExpr) resultExpression).needsCond = true; + ((LetExpr) resultExpression).needsLineNumberTableEntry = true; result = bindingContext.decorateExpression(resultExpression); } finally { currentValue = prevCurrentValue; @@ -1555,7 +1557,8 @@ public class TransPatterns extends TreeTranslator { //=> //(let T N; (let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) && /*use of N*/) for (VarSymbol vsym : hoistedVarMap.values()) { - expr = make.at(expr.pos).LetExpr(makeHoistedVarDecl(expr.pos, vsym), expr).setType(expr.type); + int pos = TreeInfo.getStartPos(expr); + expr = make.at(pos).LetExpr(makeHoistedVarDecl(pos, vsym), expr).setType(expr.type); } return expr; } 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 688ea1bd720..0b111e07c75 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -741,6 +741,11 @@ public class Gen extends JCTree.Visitor { code.resolvePending(); LetExpr tree = (LetExpr) inner_tree; + + if (tree.needsLineNumberTableEntry) { + code.statBegin(tree.pos); + } + int limit = code.nextreg; int prevLetExprStart = code.setLetExprStackPos(code.state.stacksize); try { @@ -2435,6 +2440,10 @@ public class Gen extends JCTree.Visitor { public void visitLetExpr(LetExpr tree) { code.resolvePending(); + if (tree.needsLineNumberTableEntry) { + code.statBegin(tree.pos); + } + int limit = code.nextreg; int prevLetExprStart = code.setLetExprStackPos(code.state.stacksize); try { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index f0a8b6034df..717f95390fc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -3448,6 +3448,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public JCExpression expr; /**true if a expr should be run through Gen.genCond:*/ public boolean needsCond; + public boolean needsLineNumberTableEntry; protected LetExpr(List defs, JCExpression expr) { this.defs = defs; this.expr = expr; diff --git a/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/LineNumberTestBase.java b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/LineNumberTestBase.java index 32ec2e07f73..bcd032857e5 100644 --- a/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/LineNumberTestBase.java +++ b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/LineNumberTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -92,6 +92,7 @@ public class LineNumberTestBase extends TestBase { if (expected != null) { verifyCoveredLines(methodCoveredLines, expected); + expected.validator().accept(classFile, m); } coveredLines.addAll(methodCoveredLines); diff --git a/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/PatternMatching.java b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/PatternMatching.java new file mode 100644 index 00000000000..66dd3c7ee27 --- /dev/null +++ b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/PatternMatching.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2026, 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 8387965 + * @summary Tests a line number table attribute for pattern matching + * @library /tools/lib /tools/javac/lib ../lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * java.base/jdk.internal.classfile.impl + * @build toolbox.ToolBox InMemoryFileManager TestBase + * @build LineNumberTestBase TestCase + * @run main PatternMatching + */ + +import java.lang.classfile.ClassModel; +import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeModel; +import java.lang.classfile.Instruction; +import java.lang.classfile.MethodModel; +import java.lang.classfile.instruction.LineNumber; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; + +import toolbox.ToolBox; + +public class PatternMatching extends LineNumberTestBase { + static ToolBox tb = new ToolBox(); + + public static void main(String[] args) throws Exception { + new PatternMatching().test(); + } + + public void test() throws Exception { + test(List.of(TEST_CASE)); + } + + private static final TestCase[] TEST_CASE = new TestCase[] { + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + Object obj = "abc"; // 3 + boolean isLong = obj instanceof String str // 4 + && // 5 + true; // 6 + } // 7 + } // 8 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(3, 4, 7), true, new DetailedValidator( + "line: 3", + "LDC", + "ASTORE_2", + "line: 4", + "ALOAD_2", + "INSTANCEOF", + "IFEQ", + "ALOAD_2", + "CHECKCAST", + "ASTORE", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_3", + "line: 7", + "RETURN"))), + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + Object obj = "abc"; // 3 + boolean isLong = true // 4 + && // 5 + obj instanceof String str2; // 6 + } // 7 + } // 8 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(3, 6, 7), true, new DetailedValidator( + "line: 3", + "LDC", + "ASTORE_2", + "line: 6", + "ALOAD_2", + "INSTANCEOF", + "IFEQ", + "ALOAD_2", + "CHECKCAST", + "ASTORE", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_3", + "line: 7", + "RETURN" + ))), + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + Object obj = "abc"; // 3 + boolean isLong = obj instanceof String str1 // 4 + && // 5 + obj instanceof String str2; // 6 + } // 7 + } // 8 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(3, 4, 6, 7), true, new DetailedValidator( + "line: 3", + "LDC", + "ASTORE_2", + "line: 4", + "ALOAD_2", + "INSTANCEOF", + "IFEQ", + "ALOAD_2", + "CHECKCAST", + "ASTORE", + "line: 6", + "ALOAD_2", + "INSTANCEOF", + "IFEQ", + "ALOAD_2", + "CHECKCAST", + "ASTORE", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_3", + "line: 7", + "RETURN" + ))), + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + Object obj = "abc"; // 3 + boolean isLong = obj instanceof String str // 4 + && // 5 + check(); // 6 + } // 7 + private boolean check() { return true; } // 8 + } // 9 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(3, 4, 6, 7), true, new DetailedValidator( + "line: 3", + "LDC", + "ASTORE_2", + "line: 4", + "ALOAD_2", + "INSTANCEOF", + "IFEQ", + "ALOAD_2", + "CHECKCAST", + "ASTORE", + "ALOAD_0", + "line: 6", + "INVOKEVIRTUAL", + "IFEQ", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_3", + "line: 7", + "RETURN"))), + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + Object obj = "abc"; // 3 + boolean isLong = check() // 4 + && // 5 + obj instanceof String str2; // 6 + } // 7 + private boolean check() { return true; } // 8 + } // 9 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(3, 4, 6, 7), true, new DetailedValidator( + "line: 3", + "LDC", + "ASTORE_2", + "line: 4", + "ALOAD_0", + "INVOKEVIRTUAL", + "IFEQ", + "line: 6", + "ALOAD_2", + "INSTANCEOF", + "IFEQ", + "ALOAD_2", + "CHECKCAST", + "ASTORE", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_3", + "line: 7", + "RETURN" + ))), + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + boolean isLong = obj() instanceof String str // 3 + && // 4 + true; // 5 + } // 6 + private Object obj() { return "abc"; } // 7 + } // 8 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(3, 6), true, new DetailedValidator( + "line: 3", + "ALOAD_0", + "INVOKEVIRTUAL", + "ASTORE", + "ALOAD", + "INSTANCEOF", + "IFEQ", + "ALOAD", + "CHECKCAST", + "ASTORE_3", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_2", + "line: 6", + "RETURN"))), + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + boolean isLong = true // 3 + && // 4 + obj() instanceof String str; // 5 + } // 6 + private Object obj() { return "abc"; } // 7 + } // 8 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(5, 6), true, new DetailedValidator( + "line: 5", + "ALOAD_0", + "INVOKEVIRTUAL", + "ASTORE", + "ALOAD", + "INSTANCEOF", + "IFEQ", + "ALOAD", + "CHECKCAST", + "ASTORE_3", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_2", + "line: 6", + "RETURN"))), + new TestCase(""" + public class PatternMatching { // 1 + private void test(String s) { // 2 + boolean isLong = obj() instanceof String str1 // 3 + && // 4 + obj() instanceof String str2; // 5 + } // 6 + private Object obj() { return "abc"; } // 7 + } // 8 + """, + "PatternMatching", + new TestCase.MethodData("test", List.of(3, 5, 6), true, new DetailedValidator( + "line: 3", + "ALOAD_0", + "INVOKEVIRTUAL", + "ASTORE", + "ALOAD", + "INSTANCEOF", + "IFEQ", + "ALOAD", + "CHECKCAST", + "ASTORE", + "line: 5", + "ALOAD_0", + "INVOKEVIRTUAL", + "ASTORE", + "ALOAD", + "INSTANCEOF", + "IFEQ", + "ALOAD", + "CHECKCAST", + "ASTORE_3", + "ICONST_1", + "GOTO", + "ICONST_0", + "ISTORE_2", + "line: 6", + "RETURN"))), + }; + + private static final class DetailedValidator implements BiConsumer { + + private final List expectedMethodContent; + + public DetailedValidator(String... expectedMethodContent) { + this.expectedMethodContent = List.of(expectedMethodContent); + } + + @Override + public void accept(ClassModel classFile, MethodModel m) { + CodeModel code = (CodeModel) m.code().get(); + List methodContent = new ArrayList<>(); + for (CodeElement el : code) { + switch (el) { + case Instruction instr -> methodContent.add(instr.opcode().name()); + case LineNumber ln -> methodContent.add("line: " + ln.line()); + case CodeElement _ -> {} + } + } + methodContent.forEach(System.err::println); + tb.checkEqual(expectedMethodContent, methodContent); + } + } +} diff --git a/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/TestCase.java b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/TestCase.java index 94d33c1cf8c..0bb4303f517 100644 --- a/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/TestCase.java +++ b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -21,10 +21,13 @@ * questions. */ +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.function.BiConsumer; /** * TestCase contains source code to be compiled @@ -74,7 +77,11 @@ public class TestCase { return null; } - record MethodData(String methodName, Collection expectedLines, boolean exactLines) { + record MethodData(String methodName, Collection expectedLines, boolean exactLines, BiConsumer validator) { + + public MethodData(String methodName, Collection expectedLines, boolean exactLines) { + this(methodName, expectedLines, exactLines, (_, _) -> {}); + } public MethodData { expectedLines = new HashSet<>(expectedLines);