Skip to content

Commit f5a0db4

Browse files
committed
8315447: Invalid Type Annotation attached to a method instead of a lambda
Reviewed-by: vromero
1 parent 60544a1 commit f5a0db4

File tree

3 files changed

+100
-149
lines changed

3 files changed

+100
-149
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -462,6 +462,10 @@ private void apportionTypeAnnotations(JCLambda tree,
462462
ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>();
463463

464464
for (Attribute.TypeCompound tc : source.get()) {
465+
if (tc.hasUnknownPosition()) {
466+
// Handle container annotations
467+
tc.tryFixPosition();
468+
}
465469
if (tc.position.onLambda == tree) {
466470
lambdaTypeAnnos.append(tc);
467471
} else {

test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest3.java

Lines changed: 10 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,14 +25,11 @@
2525
* @test
2626
* @bug 8005085 8005681 8008769 8010015
2727
* @summary Check (repeating)type annotations on lambda usage.
28-
* @modules jdk.jdeps/com.sun.tools.classfile
2928
* @run main CombinationsTargetTest3
3029
*/
3130

32-
import com.sun.tools.classfile.*;
3331
import java.io.*;
34-
import java.util.ArrayList;
35-
import java.util.List;
32+
import java.lang.classfile.*;
3633
import java.util.Vector;
3734

3835
public class CombinationsTargetTest3 extends ClassfileTestHelper {
@@ -203,19 +200,19 @@ else if (source.equals(srce.src6) &&
203200
classFile=new File(classdir.concat(source.altClassName));
204201
source.innerClassname=null;
205202
}
206-
ClassFile cf = ClassFile.read(classFile);
203+
ClassModel cf = ClassFile.of().parse(classFile.toPath());
207204

208-
println("Testing classfile: " + cf.getName());
205+
println("Testing classfile: " + cf.thisClass().asInternalName());
209206
//Test class,fields and method counts.
210207
test(cf);
211208

212-
for (Field f : cf.fields) {
213-
test(cf, f);
214-
test(cf, f, true);
209+
for (FieldModel f : cf.fields()) {
210+
test(f);
211+
test(f, true);
215212
}
216-
for (Method m: cf.methods) {
217-
test(cf, m);
218-
test(cf, m, true);
213+
for (MethodModel m: cf.methods()) {
214+
test(m);
215+
test(m, true);
219216
}
220217

221218
countAnnotations(); // sets errors=0 before counting.
@@ -539,139 +536,4 @@ String sourceString(String testname, String retentn, String annot2,
539536
}
540537
return imports + source;
541538
}
542-
543-
/************ Helper annotations counting methods ******************/
544-
void test(ClassFile cf) {
545-
test("CLASS",cf, null, null, Attribute.RuntimeVisibleTypeAnnotations, true);
546-
test("CLASS",cf, null, null, Attribute.RuntimeInvisibleTypeAnnotations, false);
547-
//RuntimeAnnotations since one annotation can result in two attributes.
548-
test("CLASS",cf, null, null, Attribute.RuntimeVisibleAnnotations, true);
549-
test("CLASS",cf, null, null, Attribute.RuntimeInvisibleAnnotations, false);
550-
}
551-
552-
void test(ClassFile cf, Field f, Boolean local) {
553-
if (!local) {
554-
test("FIELD",cf, f, null, Attribute.RuntimeVisibleTypeAnnotations, true);
555-
test("FIELD",cf, f, null, Attribute.RuntimeInvisibleTypeAnnotations, false);
556-
test("FIELD",cf, f, null, Attribute.RuntimeVisibleAnnotations, true);
557-
test("FIELD",cf, f, null, Attribute.RuntimeInvisibleAnnotations, false);
558-
} else {
559-
test("CODE",cf, f, null, Attribute.RuntimeVisibleTypeAnnotations, true);
560-
test("CODE",cf, f, null, Attribute.RuntimeInvisibleTypeAnnotations, false);
561-
test("CODE",cf, f, null, Attribute.RuntimeVisibleAnnotations, true);
562-
test("CODE",cf, f, null, Attribute.RuntimeInvisibleAnnotations, false);
563-
}
564-
}
565-
566-
void test(ClassFile cf, Field f) {
567-
test(cf, f, false);
568-
}
569-
570-
// 'local' determines whether to look for annotations in code attribute or not.
571-
void test(ClassFile cf, Method m, Boolean local) {
572-
if (!local) {
573-
test("METHOD",cf, null, m, Attribute.RuntimeVisibleTypeAnnotations, true);
574-
test("METHOD",cf, null, m, Attribute.RuntimeInvisibleTypeAnnotations, false);
575-
test("METHOD",cf, null, m, Attribute.RuntimeVisibleAnnotations, true);
576-
test("METHOD",cf, null, m, Attribute.RuntimeInvisibleAnnotations, false);
577-
} else {
578-
test("MCODE",cf, null, m, Attribute.RuntimeVisibleTypeAnnotations, true);
579-
test("MCODE",cf, null, m, Attribute.RuntimeInvisibleTypeAnnotations, false);
580-
test("MCODE",cf, null, m, Attribute.RuntimeVisibleAnnotations, true);
581-
test("MCODE",cf, null, m, Attribute.RuntimeInvisibleAnnotations, false);
582-
}
583-
}
584-
585-
// default to not looking in code attribute
586-
void test(ClassFile cf, Method m ) {
587-
test(cf, m, false);
588-
}
589-
590-
// Test the result of Attributes.getIndex according to expectations
591-
// encoded in the class/field/method name; increment annotations counts.
592-
void test(String ttype, ClassFile cf, Field f, Method m, String annName, boolean visible) {
593-
String testtype = ttype;
594-
String name = null;
595-
int index = -1;
596-
Attribute attr = null;
597-
Code_attribute cAttr = null;
598-
boolean isTAattr = annName.contains("TypeAnnotations");
599-
try {
600-
switch(testtype) {
601-
case "FIELD":
602-
name = f.getName(cf.constant_pool);
603-
index = f.attributes.getIndex(cf.constant_pool, annName);
604-
if(index!= -1)
605-
attr = f.attributes.get(index);
606-
break;
607-
case "CODE":
608-
name = f.getName(cf.constant_pool);
609-
//fetch index of and code attribute and annotations from code attribute.
610-
index = cf.attributes.getIndex(cf.constant_pool, Attribute.Code);
611-
if(index!= -1) {
612-
attr = cf.attributes.get(index);
613-
assert attr instanceof Code_attribute;
614-
cAttr = (Code_attribute)attr;
615-
index = cAttr.attributes.getIndex(cf.constant_pool, annName);
616-
if(index!= -1)
617-
attr = cAttr.attributes.get(index);
618-
}
619-
break;
620-
case "METHOD":
621-
name = m.getName(cf.constant_pool);
622-
index = m.attributes.getIndex(cf.constant_pool, annName);
623-
if(index!= -1)
624-
attr = m.attributes.get(index);
625-
break;
626-
case "MCODE":
627-
name = m.getName(cf.constant_pool);
628-
//fetch index of and code attribute and annotations from code attribute.
629-
index = m.attributes.getIndex(cf.constant_pool, Attribute.Code);
630-
if(index!= -1) {
631-
attr = m.attributes.get(index);
632-
assert attr instanceof Code_attribute;
633-
cAttr = (Code_attribute)attr;
634-
index = cAttr.attributes.getIndex(cf.constant_pool, annName);
635-
if(index!= -1)
636-
attr = cAttr.attributes.get(index);
637-
}
638-
break;
639-
default:
640-
name = cf.getName();
641-
index = cf.attributes.getIndex(cf.constant_pool, annName);
642-
if(index!= -1) attr = cf.attributes.get(index);
643-
}
644-
} catch(ConstantPoolException cpe) { cpe.printStackTrace(); }
645-
646-
if (index != -1) {
647-
if(isTAattr) { //count RuntimeTypeAnnotations
648-
RuntimeTypeAnnotations_attribute tAttr =
649-
(RuntimeTypeAnnotations_attribute)attr;
650-
System.out.println(testtype + ": " + name + ", " + annName + ": " +
651-
tAttr.annotations.length );
652-
if (tAttr.annotations.length > 0) {
653-
for (int i = 0; i < tAttr.annotations.length; i++) {
654-
System.out.println(" types:" + tAttr.annotations[i].position.type);
655-
}
656-
} else {
657-
System.out.println("");
658-
}
659-
allt += tAttr.annotations.length;
660-
if (visible)
661-
tvisibles += tAttr.annotations.length;
662-
else
663-
tinvisibles += tAttr.annotations.length;
664-
} else {
665-
RuntimeAnnotations_attribute tAttr =
666-
(RuntimeAnnotations_attribute)attr;
667-
System.out.println(testtype + ": " + name + ", " + annName + ": " +
668-
tAttr.annotations.length );
669-
all += tAttr.annotations.length;
670-
if (visible)
671-
visibles += tAttr.annotations.length;
672-
else
673-
invisibles += tAttr.annotations.length;
674-
}
675-
}
676-
}
677539
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8315447
27+
* @summary Container annotations for type annotations in lambdas should be
28+
* placed on the lambda method
29+
* @library /test/lib
30+
* @run junit RepeatableInLambdaTest
31+
*/
32+
33+
import java.lang.classfile.Attributes;
34+
import java.lang.classfile.ClassFile;
35+
import java.lang.constant.ClassDesc;
36+
import java.lang.reflect.AccessFlag;
37+
import java.util.Map;
38+
39+
import jdk.test.lib.compiler.InMemoryJavaCompiler;
40+
import org.junit.jupiter.api.Test;
41+
42+
import static org.junit.jupiter.api.Assertions.assertEquals;
43+
44+
class RepeatableInLambdaTest {
45+
static final String src = """
46+
import java.lang.annotation.Repeatable;
47+
import java.lang.annotation.Target;
48+
import static java.lang.annotation.ElementType.TYPE_USE;
49+
import java.util.function.Supplier;
50+
51+
@Target(TYPE_USE)
52+
@Repeatable(AC.class)
53+
@interface A {
54+
}
55+
56+
@Target(TYPE_USE)
57+
@interface AC {
58+
A[] value();
59+
}
60+
61+
@Target(TYPE_USE)
62+
@interface B {}
63+
64+
class Test {
65+
void test() {
66+
Supplier<Integer> s = () -> (@A @A @B Integer) 1;
67+
}
68+
}
69+
""";
70+
71+
@Test
72+
void test() {
73+
var codes = InMemoryJavaCompiler.compile(Map.of("Test", src));
74+
var bytes = codes.get("Test");
75+
var cf = ClassFile.of().parse(bytes);
76+
var lambdaMethod = cf.methods().stream().filter(mm -> mm.flags().has(AccessFlag.SYNTHETIC))
77+
.findFirst().orElseThrow();
78+
System.err.println(lambdaMethod);
79+
var ritva = lambdaMethod.code().orElseThrow().findAttribute(Attributes.runtimeInvisibleTypeAnnotations()).orElseThrow();
80+
var annoList = ritva.annotations();
81+
assertEquals(2, annoList.size());
82+
assertEquals(ClassDesc.of("AC"), annoList.getFirst().annotation().classSymbol());
83+
assertEquals(ClassDesc.of("B"), annoList.get(1).annotation().classSymbol());
84+
}
85+
}

0 commit comments

Comments
 (0)