From 00b869c2d481529e3bf35df84476030b272150cf Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 10:30:05 -0800 Subject: [PATCH 1/4] Backport de6667cf11aa59d1bab78ae5fb235ea0b901d5c4 --- .../com/sun/tools/javac/jvm/ClassReader.java | 301 ++++++++++++++++++ .../processing/model/type/BasicAnnoTests.java | 3 +- 2 files changed, 303 insertions(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 84195274d41..077fab6dbd6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -36,7 +36,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.IntFunction; +import java.util.function.Predicate; import javax.lang.model.element.Modifier; import javax.lang.model.element.NestingKind; @@ -2185,12 +2187,311 @@ public void run() { currentClassFile = classFile; List newList = deproxyTypeCompoundList(proxies); sym.setTypeAttributes(newList.prependList(sym.getRawTypeAttributes())); + addTypeAnnotationsToSymbol(sym, newList); } finally { currentClassFile = previousClassFile; } } } + /** + * Rewrites types in the given symbol to include type annotations. + * + *

The list of type annotations includes annotations for all types in the signature of the + * symbol. Associating the annotations with the correct type requires interpreting the JVMS + * 4.7.20-A target_type to locate the correct type to rewrite, and then interpreting the JVMS + * 4.7.20.2 type_path to associate the annotation with the correct contained type. + */ + private static void addTypeAnnotationsToSymbol( + Symbol s, List attributes) { + new TypeAnnotationSymbolVisitor(attributes).visit(s, null); + } + + private static class TypeAnnotationSymbolVisitor + extends Types.DefaultSymbolVisitor { + + private final List attributes; + + private TypeAnnotationSymbolVisitor(List attributes) { + this.attributes = attributes; + } + + @Override + public Void visitClassSymbol(Symbol.ClassSymbol s, Void unused) { + ClassType t = (ClassType) s.type; + int i = 0; + ListBuffer interfaces = new ListBuffer<>(); + for (Type itf : t.interfaces_field) { + interfaces.add(addTypeAnnotations(itf, classExtends(i++))); + } + t.interfaces_field = interfaces.toList(); + t.supertype_field = addTypeAnnotations(t.supertype_field, classExtends(65535)); + if (t.typarams_field != null) { + t.typarams_field = + rewriteTypeParameters( + t.typarams_field, TargetType.CLASS_TYPE_PARAMETER_BOUND); + } + return null; + } + + @Override + public Void visitMethodSymbol(Symbol.MethodSymbol s, Void unused) { + Type t = s.type; + if (t.hasTag(TypeTag.FORALL)) { + Type.ForAll fa = (Type.ForAll) t; + fa.tvars = rewriteTypeParameters(fa.tvars, TargetType.METHOD_TYPE_PARAMETER_BOUND); + t = fa.qtype; + } + MethodType mt = (MethodType) t; + ListBuffer argtypes = new ListBuffer<>(); + int i = 0; + for (Symbol.VarSymbol param : s.params) { + param.type = addTypeAnnotations(param.type, methodFormalParameter(i++)); + argtypes.add(param.type); + } + mt.argtypes = argtypes.toList(); + ListBuffer thrown = new ListBuffer<>(); + i = 0; + for (Type thrownType : mt.thrown) { + thrown.add(addTypeAnnotations(thrownType, thrownType(i++))); + } + mt.thrown = thrown.toList(); + mt.restype = addTypeAnnotations(mt.restype, TargetType.METHOD_RETURN); + if (mt.recvtype != null) { + mt.recvtype = addTypeAnnotations(mt.recvtype, TargetType.METHOD_RECEIVER); + } + return null; + } + + @Override + public Void visitVarSymbol(Symbol.VarSymbol s, Void unused) { + s.type = addTypeAnnotations(s.type, TargetType.FIELD); + return null; + } + + @Override + public Void visitSymbol(Symbol s, Void unused) { + return null; + } + + private List rewriteTypeParameters(List tvars, TargetType boundType) { + ListBuffer tvarbuf = new ListBuffer<>(); + int typeVariableIndex = 0; + for (Type tvar : tvars) { + Type bound = tvar.getUpperBound(); + if (bound.isCompound()) { + ClassType ct = (ClassType) bound; + int boundIndex = 0; + if (ct.supertype_field != null) { + ct.supertype_field = + addTypeAnnotations( + ct.supertype_field, + typeParameterBound( + boundType, typeVariableIndex, boundIndex++)); + } + ListBuffer itfbuf = new ListBuffer<>(); + for (Type itf : ct.interfaces_field) { + itfbuf.add( + addTypeAnnotations( + itf, + typeParameterBound( + boundType, typeVariableIndex, boundIndex++))); + } + ct.interfaces_field = itfbuf.toList(); + } else { + bound = + addTypeAnnotations( + bound, + typeParameterBound( + boundType, + typeVariableIndex, + bound.isInterface() ? 1 : 0)); + } + ((TypeVar) tvar).setUpperBound(bound); + tvarbuf.add(tvar); + typeVariableIndex++; + } + return tvarbuf.toList(); + } + + private Type addTypeAnnotations(Type type, TargetType targetType) { + return addTypeAnnotations(type, pos -> pos.type == targetType); + } + + private Type addTypeAnnotations(Type type, Predicate filter) { + Assert.checkNonNull(type); + + // Find type annotations that match the given target type + ListBuffer filtered = new ListBuffer<>(); + for (Attribute.TypeCompound attribute : this.attributes) { + if (filter.test(attribute.position)) { + filtered.add(attribute); + } + } + if (filtered.isEmpty()) { + return type; + } + + // Group the matching annotations by their type path. Each group of annotations will be + // added to a type at that location. + Map, ListBuffer> + attributesByPath = new HashMap<>(); + for (Attribute.TypeCompound attribute : filtered.toList()) { + attributesByPath + .computeIfAbsent(attribute.position.location, k -> new ListBuffer<>()) + .add(attribute); + } + + // Search the structure of the type to find the contained types at each type path + Map> attributesByType = new HashMap<>(); + new TypeAnnotationLocator(attributesByPath, attributesByType).visit(type, List.nil()); + + // Rewrite the type and add the annotations + type = new TypeAnnotationTypeMapping(attributesByType).visit(type, null); + Assert.check(attributesByType.isEmpty(), "Failed to apply annotations to types"); + + return type; + } + + private static Predicate typeParameterBound( + TargetType targetType, int parameterIndex, int boundIndex) { + return pos -> + pos.type == targetType + && pos.parameter_index == parameterIndex + && pos.bound_index == boundIndex; + } + + private static Predicate methodFormalParameter(int index) { + return pos -> + pos.type == TargetType.METHOD_FORMAL_PARAMETER && pos.parameter_index == index; + } + + private static Predicate thrownType(int index) { + return pos -> pos.type == TargetType.THROWS && pos.type_index == index; + } + + private static Predicate classExtends(int index) { + return pos -> pos.type == TargetType.CLASS_EXTENDS && pos.type_index == index; + } + } + + /** + * Visit all contained types, assembling a type path to represent the current location, and + * record the types at each type path that need to be annotated. + */ + private static class TypeAnnotationLocator + extends Types.DefaultTypeVisitor> { + private final Map, + ListBuffer> attributesByPath; + private final Map> attributesByType; + + private TypeAnnotationLocator( + Map, ListBuffer> + attributesByPath, + Map> attributesByType) { + this.attributesByPath = attributesByPath; + this.attributesByType = attributesByType; + } + + @Override + public Void visitClassType(ClassType t, List path) { + // As described in JVMS 4.7.20.2, type annotations on nested types are located with + // 'left-to-right' steps starting on 'the outermost part of the type for which a type + // annotation is admissible'. So the current path represents the outermost containing + // type of the type being visited, and we add type path steps for every contained nested + // type. + List enclosing = List.nil(); + for (Type curr = t; + curr != null && curr != Type.noType; + curr = curr.getEnclosingType()) { + enclosing = enclosing.prepend((ClassType) curr); + } + for (ClassType te : enclosing) { + if (te.typarams_field != null) { + int i = 0; + for (Type typaram : te.typarams_field) { + visit(typaram, path.append(new TypeAnnotationPosition.TypePathEntry( + TypeAnnotationPosition.TypePathEntryKind.TYPE_ARGUMENT, i++))); + } + } + visitType(te, path); + path = path.append(TypeAnnotationPosition.TypePathEntry.INNER_TYPE); + } + return null; + } + + @Override + public Void visitWildcardType( + WildcardType t, List path) { + visit(t.type, path.append(TypeAnnotationPosition.TypePathEntry.WILDCARD)); + return super.visitWildcardType(t, path); + } + + @Override + public Void visitArrayType(ArrayType t, List path) { + visit(t.elemtype, path.append(TypeAnnotationPosition.TypePathEntry.ARRAY)); + return super.visitArrayType(t, path); + } + + @Override + public Void visitType(Type t, List path) { + ListBuffer attributes = attributesByPath.remove(path); + if (attributes != null) { + attributesByType.put(t, attributes.toList()); + } + return null; + } + } + + /** A type mapping that rewrites the type to include type annotations. */ + private static class TypeAnnotationTypeMapping extends Type.StructuralTypeMapping { + + private final Map> attributesByType; + + private TypeAnnotationTypeMapping( + Map> attributesByType) { + this.attributesByType = attributesByType; + } + + private Type reannotate(T t, BiFunction f) { + // We're relying on object identify of Type instances to record where the annotations + // need to be added, so we have to retrieve the annotations for each type before + // rewriting it, and then add them after its contained types have been rewritten. + List attributes = attributesByType.remove(t); + Type mapped = f.apply(t, null); + if (attributes == null) { + return mapped; + } + // Runtime-visible and -invisible annotations are completed separately, so if the same + // type has annotations from both it will get annotated twice. + TypeMetadata.Annotations existing = mapped.getMetadata(TypeMetadata.Annotations.class); + if (existing != null) { + existing.annotationBuffer().addAll(attributes); + return mapped; + } + return mapped.annotatedType(attributes); + } + + @Override + public Type visitClassType(ClassType t, Void unused) { + return reannotate(t, super::visitClassType); + } + + @Override + public Type visitWildcardType(WildcardType t, Void unused) { + return reannotate(t, super::visitWildcardType); + } + + @Override + public Type visitArrayType(ArrayType t, Void unused) { + return reannotate(t, super::visitArrayType); + } + + @Override + public Type visitType(Type t, Void unused) { + return reannotate(t, (x, u) -> x); + } + } /************************************************************************ * Reading Symbols diff --git a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java index 85a6f6af9d8..c891a097e8b 100644 --- a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java +++ b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8013852 8031744 + * @bug 8013852 8031744 8225377 * @summary Annotations on types * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -33,6 +33,7 @@ * jdk.compiler/com.sun.tools.javac.util * @build JavacTestingAbstractProcessor DPrinter BasicAnnoTests * @compile/process -XDaccessInternalAPI -processor BasicAnnoTests -proc:only BasicAnnoTests.java + * @compile/process -XDaccessInternalAPI -processor BasicAnnoTests -proc:only BasicAnnoTests */ import java.io.PrintWriter; From 7a7d91294795ffe2783136f068ec8b9378bf0533 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 10:31:50 -0800 Subject: [PATCH 2/4] Fix backport for changes in TypeMetadata API --- .../classes/com/sun/tools/javac/jvm/ClassReader.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 077fab6dbd6..ef0e1e262ee 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -2464,10 +2464,14 @@ private Type reannotate(T t, BiFunction f) { } // Runtime-visible and -invisible annotations are completed separately, so if the same // type has annotations from both it will get annotated twice. - TypeMetadata.Annotations existing = mapped.getMetadata(TypeMetadata.Annotations.class); + TypeMetadata metadata = mapped.getMetadata(); + TypeMetadata.Annotations existing = + (TypeMetadata.Annotations) metadata.get(TypeMetadata.Entry.Kind.ANNOTATIONS); if (existing != null) { - existing.annotationBuffer().addAll(attributes); - return mapped; + TypeMetadata.Annotations combined = new TypeMetadata.Annotations( + existing.getAnnotations().appendList(attributes)); + return mapped.cloneWithMetadata( + metadata.without(TypeMetadata.Entry.Kind.ANNOTATIONS).combine(combined)); } return mapped.annotatedType(attributes); } From 6285661ee3381b50eac49c6daa7d0662ef44e052 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 10:38:27 -0800 Subject: [PATCH 3/4] Improve test coverage for class loading elements with annotations of different retentions https://github.com/openjdk/jdk17u-dev/pull/2955 --- .../javac/processing/model/type/BasicAnnoTests.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java index c891a097e8b..b218276db1f 100644 --- a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java +++ b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java @@ -41,6 +41,8 @@ import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; @@ -406,6 +408,12 @@ R scan(Iterable iter, P p) { int value(); } + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + public @interface TD { + int value(); + } + // Test cases // TODO: add more cases for arrays @@ -528,6 +536,10 @@ public class Inner6 {} @Test(posn=1, annoType=TA.class, expect="23") public Set<@TA(23) ? super Object> f9; + @Test(posn=0, annoType=TA.class, expect="1") + @Test(posn=0, annoType=TD.class, expect="2") + public @TA(1) @TD(2) int f10; + // Test type use annotations on uses of type variables @Test(posn=5, annoType = TA.class, expect = "25") @Test(posn=5, annoType = TB.class, expect = "26") From 4965dd93654d25188042ef01b5becbc76d4e29d3 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 10:39:33 -0800 Subject: [PATCH 4/4] Disable handling of annotations with different retention at the same location --- .../com/sun/tools/javac/jvm/ClassReader.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index ef0e1e262ee..506aa5b3f55 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -2465,14 +2465,14 @@ private Type reannotate(T t, BiFunction f) { // Runtime-visible and -invisible annotations are completed separately, so if the same // type has annotations from both it will get annotated twice. TypeMetadata metadata = mapped.getMetadata(); - TypeMetadata.Annotations existing = - (TypeMetadata.Annotations) metadata.get(TypeMetadata.Entry.Kind.ANNOTATIONS); - if (existing != null) { - TypeMetadata.Annotations combined = new TypeMetadata.Annotations( - existing.getAnnotations().appendList(attributes)); - return mapped.cloneWithMetadata( - metadata.without(TypeMetadata.Entry.Kind.ANNOTATIONS).combine(combined)); - } + // TypeMetadata.Annotations existing = + // (TypeMetadata.Annotations) metadata.get(TypeMetadata.Entry.Kind.ANNOTATIONS); + // if (existing != null) { + // TypeMetadata.Annotations combined = new TypeMetadata.Annotations( + // existing.getAnnotations().appendList(attributes)); + // return mapped.cloneWithMetadata( + // metadata.without(TypeMetadata.Entry.Kind.ANNOTATIONS).combine(combined)); + // } return mapped.annotatedType(attributes); }