8315447: Invalid Type Annotation attached to a method instead of a lambda

Reviewed-by: vromero
This commit is contained in:
Chen Liang 2025-03-25 19:01:22 +00:00
parent 60544a15d6
commit f5a0db43b7
3 changed files with 100 additions and 149 deletions

View File

@ -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<Attribute.TypeCompound> 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 {

View File

@ -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;
}
}
}
}

View File

@ -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<Integer> 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());
}
}