Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8336754: Remodel TypeAnnotation to "has" instead of "be" an Annotation #20247

Closed
wants to merge 20 commits into from
Closed
Changes from 7 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b5df684
8336754: Remodel TypeAnnotation to "has" instead of "be" an Annotation
liach Jul 18, 2024
6e31c48
SealedGraph now redundant
liach Jul 23, 2024
416890a
Refine the spec of TypeAnnotation per Alex feedback
liach Jul 23, 2024
beb9215
Further refine wording
liach Jul 23, 2024
84c4bfc
More about Annotation, add equals note
liach Jul 23, 2024
0e9a571
Artifact -> construct
liach Jul 23, 2024
cf16e7e
More refinements from alex
liach Jul 24, 2024
1e36ba8
Merge branch 'master' of https://github.com/openjdk/jdk into fix/type…
liach Jul 29, 2024
ef34cd3
Merge branch 'master' of https://github.com/openjdk/jdk into fix/type…
liach Aug 1, 2024
d45c35f
Improve docs for repeating, default, and value name
liach Aug 1, 2024
9ba93f4
Merge branch 'master' of https://github.com/openjdk/jdk into fix/type…
liach Aug 1, 2024
3a91a3a
remove compile, use element-value, break long sentences
liach Aug 1, 2024
db1df98
Merge branch 'master' of https://github.com/openjdk/jdk into fix/type…
liach Aug 12, 2024
e85eb40
Stage
liach Aug 13, 2024
88f64d0
Merge branch 'master' of https://github.com/openjdk/jdk into fix/type…
liach Aug 13, 2024
ef279ac
Merge branch 'master' of https://github.com/openjdk/jdk into fix/type…
liach Aug 13, 2024
92b56db
Fix terminology
liach Aug 13, 2024
c66c7d2
More bad terms and redundancy
liach Aug 13, 2024
8af0778
Missed JLS prefix from web review
liach Aug 13, 2024
802775e
Merge branch 'master' of https://github.com/openjdk/jdk into fix/type…
liach Aug 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions src/java.base/share/classes/java/lang/classfile/Annotation.java
Original file line number Diff line number Diff line change
@@ -24,10 +24,13 @@
*/
package java.lang.classfile;

import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeVisibleParameterAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
import java.lang.classfile.constantpool.Utf8Entry;
import jdk.internal.classfile.impl.AnnotationImpl;
import jdk.internal.classfile.impl.TemporaryConstantPool;
@@ -37,21 +40,58 @@
import jdk.internal.javac.PreviewFeature;

/**
* Models an annotation on a declaration.
* Models an {@code annotation} structure ({@jvms 4.7.16}) or part of a {@code
* type_annotation} structure ({@jvms 4.7.20}).
* <p>
* Each {@code annotation} structure denotes an annotation that applies to a
* construct in Java source code ({@jls 9.7.4}).
* Similarly, each {@code type_annotation} structure denotes an annotation
* that applies to a type in Java source code.
* In either case, the structure indicates the interface of the annotation
* and a set of element-value pairs.
* <p>
* The location in the class file of an {@code annotation} structure or a
* {@code type_annotation} structure,
* respectively, indicates the source code construct or type, respectively, to
* which the annotation applies.
* Accordingly, an {@code Annotation} may represent:
* <ul>
* <li>A <i>declaration annotation</i> on a class, field, method, or record
* component declaration, when an {@code annotation} structure appears in the
* {@link RuntimeVisibleAnnotationsAttribute} or
* {@link RuntimeInvisibleAnnotationsAttribute} of a class, field, method, or
* record component.
* <li>A <i>declaration annotation</i> on a method parameter declaration, when
* an {@code annotation} structure appears in the
* {@link RuntimeVisibleParameterAnnotationsAttribute} or
* {@link RuntimeInvisibleParameterAnnotationsAttribute} of a method.
* <li>The {@linkplain AnnotationValue.OfAnnotation element value} of an
* annotation, where the type of the element value is itself an annotation
* interface. In this case, the {@code annotation} structure appears as the
* {@code annotation_value} item of an {@code element_value} structure
* ({@jvms 4.7.16.1}).
* <li>A <i>type annotation</i>, when a {@code type_annotation} structure
* appears in the {@link RuntimeVisibleTypeAnnotationsAttribute}
* or {@link RuntimeInvisibleTypeAnnotationsAttribute} of a class, field,
* method, {@link CodeAttribute}, or record component.
* </ul>
* <p>
* Two {@code Annotation} objects should be compared using the {@link
* Object#equals(Object) equals} method.
*
* @see AnnotationElement
* @see AnnotationValue
* @see TypeAnnotation
* @see RuntimeVisibleAnnotationsAttribute
* @see RuntimeInvisibleAnnotationsAttribute
* @see RuntimeVisibleParameterAnnotationsAttribute
* @see RuntimeInvisibleParameterAnnotationsAttribute
*
* @sealedGraph
* @since 22
*/
@PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API)
public sealed interface Annotation
permits TypeAnnotation, AnnotationImpl {
permits AnnotationImpl {

/**
* {@return the class of the annotation}
Original file line number Diff line number Diff line change
@@ -32,7 +32,12 @@
import jdk.internal.javac.PreviewFeature;

/**
* Models a key-value pair of an annotation.
* Models a key-value pair in the {@code element_value_pairs}
* table in the {@code annotation} structure defined in
* {@jvms 4.7.16}.
* <p>
* Two {@code AnnotationElement} objects should be compared using the
* {@link Object#equals(Object) equals} method.
*
* @see Annotation
* @see AnnotationValue
Original file line number Diff line number Diff line change
@@ -40,7 +40,11 @@
import jdk.internal.javac.PreviewFeature;

/**
* Models the value of a key-value pair of an annotation.
* Models the {@code element_value} structure, or the value of a key-value
* pair of an annotation, as defined in {@jvms 4.7.16.1}.
* <p>
* Two {@code AnnotationValue} objects should be compared using the {@link
* Object#equals(Object) equals} method.
*
* @see Annotation
* @see AnnotationElement
86 changes: 35 additions & 51 deletions src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, 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,12 +25,10 @@

package java.lang.classfile;

import java.lang.constant.ClassDesc;
import java.util.List;

import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
import java.lang.classfile.constantpool.Utf8Entry;
import jdk.internal.classfile.impl.TargetInfoImpl;
import jdk.internal.classfile.impl.UnboundAttribute;

@@ -56,20 +54,41 @@
import static java.lang.classfile.ClassFile.TAT_NEW;
import static java.lang.classfile.ClassFile.TAT_RESOURCE_VARIABLE;
import static java.lang.classfile.ClassFile.TAT_THROWS;
import jdk.internal.classfile.impl.TemporaryConstantPool;
import jdk.internal.javac.PreviewFeature;

/**
* Models an annotation on a type use, as defined in {@jvms 4.7.19} and {@jvms 4.7.20}.
* Models a {@code type_annotation} structure ({@jvms 4.7.20}).
* <p>
* Each {@code type_annotation} structure denotes an annotation that applies
* to a type in Java source code ({@jls 9.7.4}). The structure indicates the
* interface of the annotation and a set of element-value pairs; this
* information is exposed by the {@link #annotation() annotation()} method.
* <p>
* The {@code type_annotation} structure is a superset of the {@code annotation}
* structure ({@jvms 4.7.16}),
* so a {@code TypeAnnotation} exposes more information about its location than
* an {@code Annotation}.
* In particular, the {@code type_annotation} structure indicates:
* <ul>
* <li>which of the <i>n</i> types in a declaration or expression is the specific
* type to which the annotation applies. This information is exposed by
* {@link #targetInfo() targetInfo()}.
* <li>whether the annotation applies to a type or to part of a type (such as a
* type argument in a parameterized type). This information is exposed by {@link
* #targetPath() targetPath()}.
* </ul>
* <p>
* Two {@code TypeAnnotation} objects should be compared using the {@link
* Object#equals(Object) equals} method.
*
* @see Annotation
* @see RuntimeVisibleTypeAnnotationsAttribute
* @see RuntimeInvisibleTypeAnnotationsAttribute
*
* @since 22
*/
@PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API)
public sealed interface TypeAnnotation
extends Annotation
permits UnboundAttribute.UnboundTypeAnnotation {

/**
@@ -170,7 +189,7 @@ public int sizeIfFixed() {

/**
* {@return information describing precisely which type in a declaration or expression
* is annotated}
* is annotated} This models the {@code target_type} and {@code target_info} items.
*/
TargetInfo targetInfo();

@@ -180,57 +199,22 @@ public int sizeIfFixed() {
List<TypePathComponent> targetPath();

/**
* {@return a type annotation}
* @param targetInfo which type in a declaration or expression is annotated
* @param targetPath which part of the type is annotated
* @param annotationClassUtf8Entry the annotation class
* @param annotationElements the annotation elements
*/
static TypeAnnotation of(TargetInfo targetInfo, List<TypePathComponent> targetPath,
Utf8Entry annotationClassUtf8Entry,
List<AnnotationElement> annotationElements) {
return new UnboundAttribute.UnboundTypeAnnotation(targetInfo, targetPath,
annotationClassUtf8Entry, annotationElements);
}

/**
* {@return a type annotation}
* @param targetInfo which type in a declaration or expression is annotated
* @param targetPath which part of the type is annotated
* @param annotationClass the annotation class
* @param annotationElements the annotation elements
* {@return the annotation applied to the part indicated by {@link #targetPath()}}
* This models the interface of the annotation and the set of element-value pairs,
* the subset of the {@code type_annotation} structure that is identical to the
* {@code annotation} structure.
*/
static TypeAnnotation of(TargetInfo targetInfo, List<TypePathComponent> targetPath,
ClassDesc annotationClass,
AnnotationElement... annotationElements) {
return of(targetInfo, targetPath, annotationClass, List.of(annotationElements));
}

/**
* {@return a type annotation}
* @param targetInfo which type in a declaration or expression is annotated
* @param targetPath which part of the type is annotated
* @param annotationClass the annotation class
* @param annotationElements the annotation elements
*/
static TypeAnnotation of(TargetInfo targetInfo, List<TypePathComponent> targetPath,
ClassDesc annotationClass,
List<AnnotationElement> annotationElements) {
return of(targetInfo, targetPath,
TemporaryConstantPool.INSTANCE.utf8Entry(annotationClass.descriptorString()), annotationElements);
}
Annotation annotation();

/**
* {@return a type annotation}
* {@return a {@code type_annotation} structure}
* @param targetInfo which type in a declaration or expression is annotated
* @param targetPath which part of the type is annotated
* @param annotationClassUtf8Entry the annotation class
* @param annotationElements the annotation elements
* @param annotation the annotation
*/
static TypeAnnotation of(TargetInfo targetInfo, List<TypePathComponent> targetPath,
Utf8Entry annotationClassUtf8Entry,
AnnotationElement... annotationElements) {
return of(targetInfo, targetPath, annotationClassUtf8Entry, List.of(annotationElements));
Annotation annotation) {
return new UnboundAttribute.UnboundTypeAnnotation(targetInfo, targetPath, annotation);
}

/**
Original file line number Diff line number Diff line change
@@ -657,7 +657,7 @@ public RuntimeInvisibleTypeAnnotationsAttribute readAttribute(AttributedElement

@Override
protected void writeBody(BufWriter buf, RuntimeInvisibleTypeAnnotationsAttribute attr) {
AnnotationReader.writeAnnotations(buf, attr.annotations());
AnnotationReader.writeTypeAnnotations(buf, attr.annotations());
}
}

@@ -714,7 +714,7 @@ public RuntimeVisibleTypeAnnotationsAttribute readAttribute(AttributedElement e,

@Override
protected void writeBody(BufWriter buf, RuntimeVisibleTypeAnnotationsAttribute attr) {
AnnotationReader.writeAnnotations(buf, attr.annotations());
AnnotationReader.writeTypeAnnotations(buf, attr.annotations());
}
}

Original file line number Diff line number Diff line change
@@ -241,10 +241,8 @@ private static TypeAnnotation readTypeAnnotation(ClassReader classReader, int p,
};
}
// the annotation info for this annotation
Utf8Entry type = classReader.readEntry(p, Utf8Entry.class);
p += 2;
return TypeAnnotation.of(targetInfo, List.of(typePath), type,
readAnnotationElementValuePairs(classReader, p));
var anno = readAnnotation(classReader, p);
return TypeAnnotation.of(targetInfo, List.of(typePath), anno);
}

private static List<TypeAnnotation.LocalVarTargetInfo> readLocalVarEntries(ClassReader classReader, int p, LabelContext lc, int targetType) {
@@ -283,20 +281,78 @@ private static int skipTypeAnnotation(ClassReader classReader, int p) {
}

public static void writeAnnotation(BufWriterImpl buf, Annotation annotation) {
// handles annotations and type annotations
// TODO annotation cleanup later
((Util.Writable) annotation).writeTo(buf);
}

public static void writeAnnotations(BufWriter buf, List<? extends Annotation> list) {
// handles annotations and type annotations
public static void writeAnnotations(BufWriter buf, List<Annotation> list) {
var internalBuf = (BufWriterImpl) buf;
internalBuf.writeU2(list.size());
for (var e : list) {
writeAnnotation(internalBuf, e);
}
}

private static int labelToBci(LabelContext lr, Label label, TypeAnnotation ta) {
//helper method to avoid NPE
if (lr == null) throw new IllegalArgumentException("Illegal targetType '%s' in TypeAnnotation outside of Code attribute".formatted(ta.targetInfo().targetType()));
return lr.labelToBci(label);
}

public static void writeTypeAnnotation(BufWriterImpl buf, TypeAnnotation ta) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to move writeTypeAnnotation from UnboundAttribute?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for consistency with reading annotations: they are now defined in the same file.

LabelContext lr = buf.labelContext();
// target_type
buf.writeU1(ta.targetInfo().targetType().targetTypeValue());

// target_info
switch (ta.targetInfo()) {
case TypeAnnotation.TypeParameterTarget tpt -> buf.writeU1(tpt.typeParameterIndex());
case TypeAnnotation.SupertypeTarget st -> buf.writeU2(st.supertypeIndex());
case TypeAnnotation.TypeParameterBoundTarget tpbt -> {
buf.writeU1(tpbt.typeParameterIndex());
buf.writeU1(tpbt.boundIndex());
}
case TypeAnnotation.EmptyTarget _ -> {
// nothing to write
}
case TypeAnnotation.FormalParameterTarget fpt -> buf.writeU1(fpt.formalParameterIndex());
case TypeAnnotation.ThrowsTarget tt -> buf.writeU2(tt.throwsTargetIndex());
case TypeAnnotation.LocalVarTarget lvt -> {
buf.writeU2(lvt.table().size());
for (var e : lvt.table()) {
int startPc = labelToBci(lr, e.startLabel(), ta);
buf.writeU2(startPc);
buf.writeU2(labelToBci(lr, e.endLabel(), ta) - startPc);
buf.writeU2(e.index());
}
}
case TypeAnnotation.CatchTarget ct -> buf.writeU2(ct.exceptionTableIndex());
case TypeAnnotation.OffsetTarget ot -> buf.writeU2(labelToBci(lr, ot.target(), ta));
case TypeAnnotation.TypeArgumentTarget tat -> {
buf.writeU2(labelToBci(lr, tat.target(), ta));
buf.writeU1(tat.typeArgumentIndex());
}
}

// target_path
buf.writeU1(ta.targetPath().size());
for (TypeAnnotation.TypePathComponent component : ta.targetPath()) {
buf.writeU1(component.typePathKind().tag());
buf.writeU1(component.typeArgumentIndex());
}

// annotation data
writeAnnotation(buf, ta.annotation());
}

public static void writeTypeAnnotations(BufWriter buf, List<TypeAnnotation> list) {
var internalBuf = (BufWriterImpl) buf;
internalBuf.writeU2(list.size());
for (var e : list) {
writeTypeAnnotation(internalBuf, e);
}
}

public static void writeAnnotationValue(BufWriterImpl buf, AnnotationValue value) {
// TODO annotation cleanup later
((Util.Writable) value).writeTo(buf);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, 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
@@ -1038,9 +1038,9 @@ private static Node annotationsToTree(String name, List<Annotation> annos) {
private static Node typeAnnotationsToTree(Style style, String name, List<TypeAnnotation> annos) {
return new ListNodeImpl(style, name, annos.stream().map(a ->
new MapNodeImpl(FLOW, "anno")
.with(leaf("annotation class", a.className().stringValue()),
.with(leaf("annotation class", a.annotation().className().stringValue()),
leaf("target info", a.targetInfo().targetType().name()))
.with(elementValuePairsToTree(a.elements()))));
.with(elementValuePairsToTree(a.annotation().elements()))));

}

Original file line number Diff line number Diff line change
@@ -401,9 +401,7 @@ AnnotationValue mapAnnotationValue(AnnotationValue val) {

List<TypeAnnotation> mapTypeAnnotations(List<TypeAnnotation> typeAnnotations) {
return typeAnnotations.stream().map(a -> TypeAnnotation.of(a.targetInfo(),
a.targetPath(), map(a.classSymbol()),
a.elements().stream().map(el -> AnnotationElement.of(el.name(),
mapAnnotationValue(el.value()))).toList())).toList();
a.targetPath(), mapAnnotation(a.annotation()))).toList();
}

List<Signature.TypeParam> mapTypeParams(List<Signature.TypeParam> typeParams) {
Loading