diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 8772e70dda3..b13c9e0fe2b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, 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 @@ -462,6 +462,10 @@ public class LambdaToMethod extends TreeTranslator { ListBuffer lambdaTypeAnnos = new ListBuffer<>(); for (Attribute.TypeCompound tc : source.get()) { + if (tc.hasUnknownPosition()) { + // Handle container annotations + tc.tryFixPosition(); + } if (tc.position.onLambda == tree) { lambdaTypeAnnos.append(tc); } else { diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest3.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest3.java index 1a8a8cf8414..5fffb82356e 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest3.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, 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 @@ -25,14 +25,11 @@ * @test * @bug 8005085 8005681 8008769 8010015 * @summary Check (repeating)type annotations on lambda usage. - * @modules jdk.jdeps/com.sun.tools.classfile * @run main CombinationsTargetTest3 */ -import com.sun.tools.classfile.*; import java.io.*; -import java.util.ArrayList; -import java.util.List; +import java.lang.classfile.*; import java.util.Vector; public class CombinationsTargetTest3 extends ClassfileTestHelper { @@ -203,19 +200,19 @@ public class CombinationsTargetTest3 extends ClassfileTestHelper { classFile=new File(classdir.concat(source.altClassName)); source.innerClassname=null; } - ClassFile cf = ClassFile.read(classFile); + ClassModel cf = ClassFile.of().parse(classFile.toPath()); - println("Testing classfile: " + cf.getName()); + println("Testing classfile: " + cf.thisClass().asInternalName()); //Test class,fields and method counts. test(cf); - for (Field f : cf.fields) { - test(cf, f); - test(cf, f, true); + for (FieldModel f : cf.fields()) { + test(f); + test(f, true); } - for (Method m: cf.methods) { - test(cf, m); - test(cf, m, true); + for (MethodModel m: cf.methods()) { + test(m); + test(m, true); } countAnnotations(); // sets errors=0 before counting. @@ -539,139 +536,4 @@ public class CombinationsTargetTest3 extends ClassfileTestHelper { } return imports + source; } - - /************ Helper annotations counting methods ******************/ - void test(ClassFile cf) { - test("CLASS",cf, null, null, Attribute.RuntimeVisibleTypeAnnotations, true); - test("CLASS",cf, null, null, Attribute.RuntimeInvisibleTypeAnnotations, false); - //RuntimeAnnotations since one annotation can result in two attributes. - test("CLASS",cf, null, null, Attribute.RuntimeVisibleAnnotations, true); - test("CLASS",cf, null, null, Attribute.RuntimeInvisibleAnnotations, false); - } - - void test(ClassFile cf, Field f, Boolean local) { - if (!local) { - test("FIELD",cf, f, null, Attribute.RuntimeVisibleTypeAnnotations, true); - test("FIELD",cf, f, null, Attribute.RuntimeInvisibleTypeAnnotations, false); - test("FIELD",cf, f, null, Attribute.RuntimeVisibleAnnotations, true); - test("FIELD",cf, f, null, Attribute.RuntimeInvisibleAnnotations, false); - } else { - test("CODE",cf, f, null, Attribute.RuntimeVisibleTypeAnnotations, true); - test("CODE",cf, f, null, Attribute.RuntimeInvisibleTypeAnnotations, false); - test("CODE",cf, f, null, Attribute.RuntimeVisibleAnnotations, true); - test("CODE",cf, f, null, Attribute.RuntimeInvisibleAnnotations, false); - } - } - - void test(ClassFile cf, Field f) { - test(cf, f, false); - } - - // 'local' determines whether to look for annotations in code attribute or not. - void test(ClassFile cf, Method m, Boolean local) { - if (!local) { - test("METHOD",cf, null, m, Attribute.RuntimeVisibleTypeAnnotations, true); - test("METHOD",cf, null, m, Attribute.RuntimeInvisibleTypeAnnotations, false); - test("METHOD",cf, null, m, Attribute.RuntimeVisibleAnnotations, true); - test("METHOD",cf, null, m, Attribute.RuntimeInvisibleAnnotations, false); - } else { - test("MCODE",cf, null, m, Attribute.RuntimeVisibleTypeAnnotations, true); - test("MCODE",cf, null, m, Attribute.RuntimeInvisibleTypeAnnotations, false); - test("MCODE",cf, null, m, Attribute.RuntimeVisibleAnnotations, true); - test("MCODE",cf, null, m, Attribute.RuntimeInvisibleAnnotations, false); - } - } - - // default to not looking in code attribute - void test(ClassFile cf, Method m ) { - test(cf, m, false); - } - - // Test the result of Attributes.getIndex according to expectations - // encoded in the class/field/method name; increment annotations counts. - void test(String ttype, ClassFile cf, Field f, Method m, String annName, boolean visible) { - String testtype = ttype; - String name = null; - int index = -1; - Attribute attr = null; - Code_attribute cAttr = null; - boolean isTAattr = annName.contains("TypeAnnotations"); - try { - switch(testtype) { - case "FIELD": - name = f.getName(cf.constant_pool); - index = f.attributes.getIndex(cf.constant_pool, annName); - if(index!= -1) - attr = f.attributes.get(index); - break; - case "CODE": - name = f.getName(cf.constant_pool); - //fetch index of and code attribute and annotations from code attribute. - index = cf.attributes.getIndex(cf.constant_pool, Attribute.Code); - if(index!= -1) { - attr = cf.attributes.get(index); - assert attr instanceof Code_attribute; - cAttr = (Code_attribute)attr; - index = cAttr.attributes.getIndex(cf.constant_pool, annName); - if(index!= -1) - attr = cAttr.attributes.get(index); - } - break; - case "METHOD": - name = m.getName(cf.constant_pool); - index = m.attributes.getIndex(cf.constant_pool, annName); - if(index!= -1) - attr = m.attributes.get(index); - break; - case "MCODE": - name = m.getName(cf.constant_pool); - //fetch index of and code attribute and annotations from code attribute. - index = m.attributes.getIndex(cf.constant_pool, Attribute.Code); - if(index!= -1) { - attr = m.attributes.get(index); - assert attr instanceof Code_attribute; - cAttr = (Code_attribute)attr; - index = cAttr.attributes.getIndex(cf.constant_pool, annName); - if(index!= -1) - attr = cAttr.attributes.get(index); - } - break; - default: - name = cf.getName(); - index = cf.attributes.getIndex(cf.constant_pool, annName); - if(index!= -1) attr = cf.attributes.get(index); - } - } catch(ConstantPoolException cpe) { cpe.printStackTrace(); } - - if (index != -1) { - if(isTAattr) { //count RuntimeTypeAnnotations - RuntimeTypeAnnotations_attribute tAttr = - (RuntimeTypeAnnotations_attribute)attr; - System.out.println(testtype + ": " + name + ", " + annName + ": " + - tAttr.annotations.length ); - if (tAttr.annotations.length > 0) { - for (int i = 0; i < tAttr.annotations.length; i++) { - System.out.println(" types:" + tAttr.annotations[i].position.type); - } - } else { - System.out.println(""); - } - allt += tAttr.annotations.length; - if (visible) - tvisibles += tAttr.annotations.length; - else - tinvisibles += tAttr.annotations.length; - } else { - RuntimeAnnotations_attribute tAttr = - (RuntimeAnnotations_attribute)attr; - System.out.println(testtype + ": " + name + ", " + annName + ": " + - tAttr.annotations.length ); - all += tAttr.annotations.length; - if (visible) - visibles += tAttr.annotations.length; - else - invisibles += tAttr.annotations.length; - } - } - } } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/RepeatableInLambdaTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/RepeatableInLambdaTest.java new file mode 100644 index 00000000000..7c7620dd227 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/RepeatableInLambdaTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025, 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 8315447 + * @summary Container annotations for type annotations in lambdas should be + * placed on the lambda method + * @library /test/lib + * @run junit RepeatableInLambdaTest + */ + +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.constant.ClassDesc; +import java.lang.reflect.AccessFlag; +import java.util.Map; + +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class RepeatableInLambdaTest { + static final String src = """ + import java.lang.annotation.Repeatable; + import java.lang.annotation.Target; + import static java.lang.annotation.ElementType.TYPE_USE; + import java.util.function.Supplier; + + @Target(TYPE_USE) + @Repeatable(AC.class) + @interface A { + } + + @Target(TYPE_USE) + @interface AC { + A[] value(); + } + + @Target(TYPE_USE) + @interface B {} + + class Test { + void test() { + Supplier s = () -> (@A @A @B Integer) 1; + } + } + """; + + @Test + void test() { + var codes = InMemoryJavaCompiler.compile(Map.of("Test", src)); + var bytes = codes.get("Test"); + var cf = ClassFile.of().parse(bytes); + var lambdaMethod = cf.methods().stream().filter(mm -> mm.flags().has(AccessFlag.SYNTHETIC)) + .findFirst().orElseThrow(); + System.err.println(lambdaMethod); + var ritva = lambdaMethod.code().orElseThrow().findAttribute(Attributes.runtimeInvisibleTypeAnnotations()).orElseThrow(); + var annoList = ritva.annotations(); + assertEquals(2, annoList.size()); + assertEquals(ClassDesc.of("AC"), annoList.getFirst().annotation().classSymbol()); + assertEquals(ClassDesc.of("B"), annoList.get(1).annotation().classSymbol()); + } +}