mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-17 17:07:53 +00:00
8315447: Invalid Type Annotation attached to a method instead of a lambda
Reviewed-by: vromero
This commit is contained in:
parent
60544a15d6
commit
f5a0db43b7
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user