This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push:
new 67fca5a6 Sort members
67fca5a6 is described below
commit 67fca5a65b9a69cf0fe1aae696e539120e3bcff0
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Oct 30 08:06:23 2026 -0400
Sort members
---
.../apache/bcel/classfile/DescendingVisitor.java | 18 +-
.../org/apache/bcel/classfile/EmptyVisitor.java | 10 +-
.../apache/bcel/classfile/TypeAnnotationEntry.java | 112 ++++-----
.../org/apache/bcel/classfile/TypeAnnotations.java | 32 +--
.../java/org/apache/bcel/classfile/Visitor.java | 20 +-
.../verifier/statics/StringRepresentation.java | 20 +-
.../classfile/TypeAnnotationAttributesTest.java | 46 ++--
.../bcel/classfile/TypeAnnotationEntryTest.java | 178 +++++++-------
.../apache/bcel/classfile/TypeAnnotationsTest.java | 266 ++++++++++-----------
9 files changed, 351 insertions(+), 351 deletions(-)
diff --git a/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java
b/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java
index 6fadac04..be2a7b23 100644
--- a/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java
+++ b/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java
@@ -537,15 +537,6 @@ public class DescendingVisitor implements Visitor {
stack.pop();
}
- /** @since 6.14.0 */
- @Override
- public void visitTypeAnnotations(final TypeAnnotations obj) {
- stack.push(obj);
- obj.accept(visitor);
-
accept(Stream.of(obj.getTypeAnnotationEntries()).map(TypeAnnotationEntry::getAnnotationEntry).toArray(AnnotationEntry[]::new));
- stack.pop();
- }
-
@Override
public void visitSignature(final Signature attribute) {
stack.push(attribute);
@@ -597,6 +588,15 @@ public class DescendingVisitor implements Visitor {
stack.pop();
}
+ /** @since 6.14.0 */
+ @Override
+ public void visitTypeAnnotations(final TypeAnnotations obj) {
+ stack.push(obj);
+ obj.accept(visitor);
+
accept(Stream.of(obj.getTypeAnnotationEntries()).map(TypeAnnotationEntry::getAnnotationEntry).toArray(AnnotationEntry[]::new));
+ stack.pop();
+ }
+
@Override
public void visitUnknown(final Unknown attribute) {
stack.push(attribute);
diff --git a/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java
b/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java
index a34449e3..a230fb0f 100644
--- a/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java
+++ b/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java
@@ -303,11 +303,6 @@ public class EmptyVisitor implements Visitor {
public void visitPermittedSubclasses(final PermittedSubclasses obj) {
}
- /** @since 6.14.0 */
- @Override
- public void visitTypeAnnotations(final TypeAnnotations obj) {
- }
-
@Override
public void visitSignature(final Signature obj) {
}
@@ -338,6 +333,11 @@ public class EmptyVisitor implements Visitor {
public void visitSynthetic(final Synthetic obj) {
}
+ /** @since 6.14.0 */
+ @Override
+ public void visitTypeAnnotations(final TypeAnnotations obj) {
+ }
+
@Override
public void visitUnknown(final Unknown obj) {
}
diff --git a/src/main/java/org/apache/bcel/classfile/TypeAnnotationEntry.java
b/src/main/java/org/apache/bcel/classfile/TypeAnnotationEntry.java
index 5c7caf61..1a41f499 100644
--- a/src/main/java/org/apache/bcel/classfile/TypeAnnotationEntry.java
+++ b/src/main/java/org/apache/bcel/classfile/TypeAnnotationEntry.java
@@ -115,47 +115,6 @@ public class TypeAnnotationEntry {
/** Number of bytes of a {@code localvar_target} entry: start_pc, length
and index. */
private static final int LOCAL_VARIABLE_RANGE_SIZE = 6;
- /**
- * Gets the number of bytes of the target info that follows a target type.
- *
- * @param targetType The target type.
- * @return the size of the target info, or -1 if it depends on its content
(the table of a {@code localvar_target}).
- * @throws ClassFormatException Thrown if the target type is not defined.
- */
- private static int targetInfoSize(final int targetType) {
- switch (targetType) {
- case TARGET_CLASS_TYPE_PARAMETER:
- case TARGET_METHOD_TYPE_PARAMETER:
- case TARGET_METHOD_FORMAL_PARAMETER:
- return 1;
- case TARGET_CLASS_EXTENDS:
- case TARGET_CLASS_TYPE_PARAMETER_BOUND:
- case TARGET_METHOD_TYPE_PARAMETER_BOUND:
- case TARGET_THROWS:
- case TARGET_EXCEPTION_PARAMETER:
- case TARGET_INSTANCEOF:
- case TARGET_NEW:
- case TARGET_CONSTRUCTOR_REFERENCE:
- case TARGET_METHOD_REFERENCE:
- return 2;
- case TARGET_FIELD:
- case TARGET_METHOD_RETURN:
- case TARGET_METHOD_RECEIVER:
- return 0;
- case TARGET_LOCAL_VARIABLE:
- case TARGET_RESOURCE_VARIABLE:
- return -1;
- case TARGET_CAST:
- case TARGET_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
- case TARGET_METHOD_INVOCATION_TYPE_ARGUMENT:
- case TARGET_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
- case TARGET_METHOD_REFERENCE_TYPE_ARGUMENT:
- return 3;
- default:
- throw new ClassFormatException("Invalid type annotation target
type: 0x" + Integer.toHexString(targetType));
- }
- }
-
private static void checkTargetInfo(final int targetType, final byte[]
targetInfo) {
final int size = targetInfoSize(targetType);
if (size >= 0) {
@@ -197,6 +156,47 @@ public class TypeAnnotationEntry {
return new TypeAnnotationEntry(targetType, targetInfo, typePath,
AnnotationEntry.read(input, constantPool, isRuntimeVisible));
}
+ /**
+ * Gets the number of bytes of the target info that follows a target type.
+ *
+ * @param targetType The target type.
+ * @return the size of the target info, or -1 if it depends on its content
(the table of a {@code localvar_target}).
+ * @throws ClassFormatException Thrown if the target type is not defined.
+ */
+ private static int targetInfoSize(final int targetType) {
+ switch (targetType) {
+ case TARGET_CLASS_TYPE_PARAMETER:
+ case TARGET_METHOD_TYPE_PARAMETER:
+ case TARGET_METHOD_FORMAL_PARAMETER:
+ return 1;
+ case TARGET_CLASS_EXTENDS:
+ case TARGET_CLASS_TYPE_PARAMETER_BOUND:
+ case TARGET_METHOD_TYPE_PARAMETER_BOUND:
+ case TARGET_THROWS:
+ case TARGET_EXCEPTION_PARAMETER:
+ case TARGET_INSTANCEOF:
+ case TARGET_NEW:
+ case TARGET_CONSTRUCTOR_REFERENCE:
+ case TARGET_METHOD_REFERENCE:
+ return 2;
+ case TARGET_FIELD:
+ case TARGET_METHOD_RETURN:
+ case TARGET_METHOD_RECEIVER:
+ return 0;
+ case TARGET_LOCAL_VARIABLE:
+ case TARGET_RESOURCE_VARIABLE:
+ return -1;
+ case TARGET_CAST:
+ case TARGET_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
+ case TARGET_METHOD_INVOCATION_TYPE_ARGUMENT:
+ case TARGET_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
+ case TARGET_METHOD_REFERENCE_TYPE_ARGUMENT:
+ return 3;
+ default:
+ throw new ClassFormatException("Invalid type annotation target
type: 0x" + Integer.toHexString(targetType));
+ }
+ }
+
private final int targetType;
private final byte[] targetInfo;
@@ -291,18 +291,13 @@ public class TypeAnnotationEntry {
}
/**
- * Gets the number of steps in the type path.
- *
- * <p>
- * The path leads from the type of the target to the part of it that is
annotated. An empty path identifies the outermost part of the type where an
- * annotation is allowed. For a non-static inner class, {@code @A
Outer.Inner} has an empty path, while {@code Outer.@A Inner} requires one step.
- * For a static nested class, {@code Outer.@A Nested} has an empty path.
- * </p>
+ * Gets the index of the type argument for a step of the type path of kind
3, else 0.
*
- * @return The number of steps of the type path.
+ * @param step Index of the step, from 0 to {@link #getTypePathLength()}
excluded.
+ * @return The index of the type argument.
*/
- public int getTypePathLength() {
- return typePath.length / 2;
+ public int getTypePathArgumentIndex(final int step) {
+ return typePath[step * 2 + 1] & 0xFF;
}
/**
@@ -317,13 +312,18 @@ public class TypeAnnotationEntry {
}
/**
- * Gets the index of the type argument for a step of the type path of kind
3, else 0.
+ * Gets the number of steps in the type path.
*
- * @param step Index of the step, from 0 to {@link #getTypePathLength()}
excluded.
- * @return The index of the type argument.
+ * <p>
+ * The path leads from the type of the target to the part of it that is
annotated. An empty path identifies the outermost part of the type where an
+ * annotation is allowed. For a non-static inner class, {@code @A
Outer.Inner} has an empty path, while {@code Outer.@A Inner} requires one step.
+ * For a static nested class, {@code Outer.@A Nested} has an empty path.
+ * </p>
+ *
+ * @return The number of steps of the type path.
*/
- public int getTypePathArgumentIndex(final int step) {
- return typePath[step * 2 + 1] & 0xFF;
+ public int getTypePathLength() {
+ return typePath.length / 2;
}
@Override
diff --git a/src/main/java/org/apache/bcel/classfile/TypeAnnotations.java
b/src/main/java/org/apache/bcel/classfile/TypeAnnotations.java
index aa13deaf..4ffc4aaa 100644
--- a/src/main/java/org/apache/bcel/classfile/TypeAnnotations.java
+++ b/src/main/java/org/apache/bcel/classfile/TypeAnnotations.java
@@ -48,15 +48,19 @@ public abstract class TypeAnnotations extends Attribute
implements Iterable<Type
* @param annotationType The subclass type of the attribute.
* @param nameIndex Index pointing to the name of the attribute.
* @param length Content length in bytes.
- * @param typeAnnotationTable The actual type annotations.
+ * @param input Input stream.
* @param constantPool Array of constants.
* @param isRuntimeVisible whether the annotations are visible at runtime.
+ * @throws IOException Thrown if an I/O error occurs.
*/
- public TypeAnnotations(final byte annotationType, final int nameIndex,
final int length, final TypeAnnotationEntry[] typeAnnotationTable,
- final ConstantPool constantPool, final boolean isRuntimeVisible) {
- super(annotationType, nameIndex, length, constantPool);
- setTypeAnnotationTable(typeAnnotationTable);
- this.isRuntimeVisible = isRuntimeVisible;
+ TypeAnnotations(final byte annotationType, final int nameIndex, final int
length, final DataInput input, final ConstantPool constantPool,
+ final boolean isRuntimeVisible) throws IOException {
+ this(annotationType, nameIndex, length, (TypeAnnotationEntry[]) null,
constantPool, isRuntimeVisible);
+ final int tableLength = input.readUnsignedShort();
+ typeAnnotationTable = new TypeAnnotationEntry[tableLength];
+ for (int i = 0; i < tableLength; i++) {
+ typeAnnotationTable[i] = TypeAnnotationEntry.read(input,
constantPool, isRuntimeVisible);
+ }
}
/**
@@ -65,19 +69,15 @@ public abstract class TypeAnnotations extends Attribute
implements Iterable<Type
* @param annotationType The subclass type of the attribute.
* @param nameIndex Index pointing to the name of the attribute.
* @param length Content length in bytes.
- * @param input Input stream.
+ * @param typeAnnotationTable The actual type annotations.
* @param constantPool Array of constants.
* @param isRuntimeVisible whether the annotations are visible at runtime.
- * @throws IOException Thrown if an I/O error occurs.
*/
- TypeAnnotations(final byte annotationType, final int nameIndex, final int
length, final DataInput input, final ConstantPool constantPool,
- final boolean isRuntimeVisible) throws IOException {
- this(annotationType, nameIndex, length, (TypeAnnotationEntry[]) null,
constantPool, isRuntimeVisible);
- final int tableLength = input.readUnsignedShort();
- typeAnnotationTable = new TypeAnnotationEntry[tableLength];
- for (int i = 0; i < tableLength; i++) {
- typeAnnotationTable[i] = TypeAnnotationEntry.read(input,
constantPool, isRuntimeVisible);
- }
+ public TypeAnnotations(final byte annotationType, final int nameIndex,
final int length, final TypeAnnotationEntry[] typeAnnotationTable,
+ final ConstantPool constantPool, final boolean isRuntimeVisible) {
+ super(annotationType, nameIndex, length, constantPool);
+ setTypeAnnotationTable(typeAnnotationTable);
+ this.isRuntimeVisible = isRuntimeVisible;
}
@Override
diff --git a/src/main/java/org/apache/bcel/classfile/Visitor.java
b/src/main/java/org/apache/bcel/classfile/Visitor.java
index 6c8608af..b49aadb5 100644
--- a/src/main/java/org/apache/bcel/classfile/Visitor.java
+++ b/src/main/java/org/apache/bcel/classfile/Visitor.java
@@ -437,16 +437,6 @@ public interface Visitor {
// empty
}
- /**
- * Visits a RuntimeVisibleTypeAnnotations or
RuntimeInvisibleTypeAnnotations attribute.
- *
- * @param obj The attribute.
- * @since 6.14.0
- */
- default void visitTypeAnnotations(final TypeAnnotations obj) {
- // empty
- }
-
/**
* Visits a {@link Record} object.
*
@@ -512,6 +502,16 @@ public interface Visitor {
*/
void visitSynthetic(Synthetic obj);
+ /**
+ * Visits a RuntimeVisibleTypeAnnotations or
RuntimeInvisibleTypeAnnotations attribute.
+ *
+ * @param obj The attribute.
+ * @since 6.14.0
+ */
+ default void visitTypeAnnotations(final TypeAnnotations obj) {
+ // empty
+ }
+
/**
* Visits an Unknown attribute.
*
diff --git
a/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
b/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
index 8819b4f0..8ded2ae5 100644
--- a/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
+++ b/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
@@ -413,16 +413,6 @@ public class StringRepresentation extends
org.apache.bcel.classfile.EmptyVisitor
tostring = toString(obj);
}
- /**
- * Visits RuntimeVisibleTypeAnnotations and
RuntimeInvisibleTypeAnnotations attributes.
- *
- * @since 6.14.0
- */
- @Override
- public void visitTypeAnnotations(final TypeAnnotations obj) {
- tostring = toString(obj);
- }
-
@Override
public void visitRecord(final Record obj) {
tostring = toString(obj);
@@ -461,6 +451,16 @@ public class StringRepresentation extends
org.apache.bcel.classfile.EmptyVisitor
tostring = toString(obj);
}
+ /**
+ * Visits RuntimeVisibleTypeAnnotations and
RuntimeInvisibleTypeAnnotations attributes.
+ *
+ * @since 6.14.0
+ */
+ @Override
+ public void visitTypeAnnotations(final TypeAnnotations obj) {
+ tostring = toString(obj);
+ }
+
@Override
public void visitUnknown(final Unknown obj) {
tostring = toString(obj);
diff --git
a/src/test/java/org/apache/bcel/classfile/TypeAnnotationAttributesTest.java
b/src/test/java/org/apache/bcel/classfile/TypeAnnotationAttributesTest.java
index 8dfad67b..301d4477 100644
--- a/src/test/java/org/apache/bcel/classfile/TypeAnnotationAttributesTest.java
+++ b/src/test/java/org/apache/bcel/classfile/TypeAnnotationAttributesTest.java
@@ -151,29 +151,6 @@ class TypeAnnotationAttributesTest {
assertEquals(2, original.getNumTypeAnnotations());
}
- @ParameterizedTest
- @ValueSource(booleans = {false, true})
- void readsEmptyAttributes(final boolean visible) throws IOException {
- final Fixture fixture = new Fixture(visible);
- final byte[] encoded = fixture.annotationAttribute(0x13, new byte[0],
0);
- final TypeAnnotations annotations = (TypeAnnotations) parse(encoded,
fixture.pool);
- assertEquals(0, annotations.getNumTypeAnnotations());
- assertFalse(annotations.iterator().hasNext());
- assertArrayEquals(encoded, dump(annotations));
- assertArrayEquals(encoded,
dump(annotations.copy(fixture.pool.copy())));
- }
-
- @ParameterizedTest
- @ValueSource(booleans = {false, true})
- void rejectsTruncatedAttributes(final boolean visible) throws IOException {
- final Fixture fixture = new Fixture(visible);
- final byte[] encoded = fixture.annotationAttribute(0x13, new byte[0],
2);
- for (int length = 0; length < encoded.length; length++) {
- final byte[] truncated = Arrays.copyOf(encoded, length);
- assertThrows(EOFException.class, () -> parse(truncated,
fixture.pool), "length " + length);
- }
- }
-
@ParameterizedTest
@ValueSource(booleans = {false, true})
void enforcesTheUnsignedAnnotationCount(final boolean visible) throws
IOException {
@@ -191,6 +168,18 @@ class TypeAnnotationAttributesTest {
assertThrows(ClassFormatException.class, () -> dump(annotations));
}
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ void readsEmptyAttributes(final boolean visible) throws IOException {
+ final Fixture fixture = new Fixture(visible);
+ final byte[] encoded = fixture.annotationAttribute(0x13, new byte[0],
0);
+ final TypeAnnotations annotations = (TypeAnnotations) parse(encoded,
fixture.pool);
+ assertEquals(0, annotations.getNumTypeAnnotations());
+ assertFalse(annotations.iterator().hasNext());
+ assertArrayEquals(encoded, dump(annotations));
+ assertArrayEquals(encoded,
dump(annotations.copy(fixture.pool.copy())));
+ }
+
@ParameterizedTest
@ValueSource(booleans = {false, true})
void readsTypeAnnotationsInsideCode(final boolean visible) throws
IOException {
@@ -242,4 +231,15 @@ class TypeAnnotationAttributesTest {
assertEquals(0x13,
annotations.getTypeAnnotationEntries()[0].getTargetType());
assertArrayEquals(encoded, dump(record));
}
+
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ void rejectsTruncatedAttributes(final boolean visible) throws IOException {
+ final Fixture fixture = new Fixture(visible);
+ final byte[] encoded = fixture.annotationAttribute(0x13, new byte[0],
2);
+ for (int length = 0; length < encoded.length; length++) {
+ final byte[] truncated = Arrays.copyOf(encoded, length);
+ assertThrows(EOFException.class, () -> parse(truncated,
fixture.pool), "length " + length);
+ }
+ }
}
diff --git
a/src/test/java/org/apache/bcel/classfile/TypeAnnotationEntryTest.java
b/src/test/java/org/apache/bcel/classfile/TypeAnnotationEntryTest.java
index a938e597..76bf2100 100644
--- a/src/test/java/org/apache/bcel/classfile/TypeAnnotationEntryTest.java
+++ b/src/test/java/org/apache/bcel/classfile/TypeAnnotationEntryTest.java
@@ -108,6 +108,67 @@ class TypeAnnotationEntryTest {
Arguments.of(TypeAnnotationEntry.TARGET_METHOD_REFERENCE_TYPE_ARGUMENT, 0x4B,
new byte[] {19, 20, (byte) 255}));
}
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ void deeplyCopiesNestedAnnotationValues(final boolean visible) throws
IOException {
+ final ConstantPoolGen gen = new ConstantPoolGen();
+ final int type = gen.addUtf8("Lexample/Annotation;");
+ final int nestedType = gen.addUtf8("Lexample/Nested;");
+ final int arrayName = gen.addUtf8("items");
+ final int nestedName = gen.addUtf8("nested");
+ final int valueName = gen.addUtf8("value");
+ final int first = gen.addUtf8("first");
+ final int second = gen.addUtf8("second");
+ final ConstantPool pool = gen.getFinalConstantPool();
+ final AnnotationEntry nested = new AnnotationEntry(nestedType, pool,
visible);
+ nested.addElementNameValuePair(new ElementValuePair(valueName, new
SimpleElementValue(ElementValue.STRING, first, pool), pool));
+ final AnnotationEntry annotation = new AnnotationEntry(type, pool,
visible);
+ annotation.addElementNameValuePair(new ElementValuePair(arrayName, new
ArrayElementValue(ElementValue.ARRAY,
+ new ElementValue[] {new
SimpleElementValue(ElementValue.STRING, first, pool)}, pool), pool));
+ annotation.addElementNameValuePair(new ElementValuePair(nestedName,
new AnnotationElementValue(ElementValue.ANNOTATION, nested, pool), pool));
+ final TypeAnnotationEntry original = new TypeAnnotationEntry(0x13, new
byte[0], TYPE_PATH, annotation);
+ final byte[] expected = dump(original);
+ final ConstantPool otherPool = pool.copy();
+ final TypeAnnotationEntry copy = original.copy(otherPool);
+ assertArrayEquals(expected, dump(copy));
+ assertNotSame(original, copy);
+ assertNotSame(annotation, copy.getAnnotationEntry());
+ assertSame(otherPool, copy.getAnnotationEntry().getConstantPool());
+ assertEquals(visible, copy.getAnnotationEntry().isRuntimeVisible());
+ final ElementValuePair[] copiedPairs =
copy.getAnnotationEntry().getElementValuePairs();
+ final ArrayElementValue array = (ArrayElementValue)
copiedPairs[0].getValue();
+ final AnnotationEntry copiedNested = ((AnnotationElementValue)
copiedPairs[1].getValue()).getAnnotationEntry();
+ assertNotSame(annotation.getElementValuePairs()[0], copiedPairs[0]);
+ assertNotSame(((ArrayElementValue)
annotation.getElementValuePairs()[0].getValue()).getElementValuesArray(),
array.getElementValuesArray());
+ assertNotSame(nested, copiedNested);
+ assertSame(otherPool, array.getConstantPool());
+ assertSame(otherPool,
array.getElementValuesArray()[0].getConstantPool());
+ assertSame(otherPool, copiedNested.getConstantPool());
+ assertSame(otherPool,
copiedNested.getElementValuePairs()[0].getValue().getConstantPool());
+ assertEquals(visible, copiedNested.isRuntimeVisible());
+ ((SimpleElementValue)
array.getElementValuesArray()[0]).setIndex(second);
+ ((SimpleElementValue)
copiedNested.getElementValuePairs()[0].getValue()).setIndex(second);
+ assertEquals("second",
array.getElementValuesArray()[0].stringifyValue());
+ assertEquals("second",
copiedNested.getElementValuePairs()[0].getValue().stringifyValue());
+ assertArrayEquals(expected, dump(original), "Mutating nested copied
values must not change the original");
+ }
+
+ @Test
+ void protectsTargetInfoAndPathFromCallerMutation() throws IOException {
+ final byte[] info = {0, 2};
+ final byte[] path = {3, 1};
+ final TypeAnnotationEntry entry = new TypeAnnotationEntry(0x10, info,
path, new AnnotationEntry(1, constantPool(), true));
+ info[0] = 99;
+ path[0] = 99;
+ path[1] = 99;
+ final byte[] returnedInfo = entry.getTargetInfo();
+ returnedInfo[0] = 88;
+ assertArrayEquals(new byte[] {0, 2}, entry.getTargetInfo());
+ assertEquals(3, entry.getTypePathKind(0));
+ assertEquals(1, entry.getTypePathArgumentIndex(0));
+ assertArrayEquals(encodedEntry(0x10, new byte[] {0, 2}, new byte[] {3,
1}), dump(entry));
+ }
+
@ParameterizedTest
@MethodSource("targetLayouts")
void readsAndDumpsEveryTargetLayout(final int targetConstant, final int
tag, final byte[] info) throws IOException {
@@ -143,29 +204,6 @@ class TypeAnnotationEntryTest {
assertArrayEquals(expected, dump(entry));
}
- @ParameterizedTest
- @MethodSource("targetLayouts")
- void rejectsTruncatedEntries(final int targetConstant, final int tag,
final byte[] info) throws IOException {
- final byte[] bytes = encodedEntry(tag, info, TYPE_PATH);
- for (int length = 0; length < bytes.length; length++) {
- final byte[] truncated = Arrays.copyOf(bytes, length);
- assertThrows(EOFException.class, () -> read(truncated, true),
"target " + targetConstant + ", length " + length);
- }
- }
-
- @ParameterizedTest
- @MethodSource("targetLayouts")
- void rejectsIncorrectTargetInfoLengths(final int targetConstant, final int
tag, final byte[] info) {
- final AnnotationEntry annotation = new AnnotationEntry(1,
constantPool(), true);
- assertEquals(tag, targetConstant);
- assertThrows(IllegalArgumentException.class,
- () -> new TypeAnnotationEntry(targetConstant,
Arrays.copyOf(info, info.length + 1), new byte[0], annotation));
- if (info.length > 0) {
- assertThrows(IllegalArgumentException.class,
- () -> new TypeAnnotationEntry(targetConstant,
Arrays.copyOf(info, info.length - 1), new byte[0], annotation));
- }
- }
-
@ParameterizedTest
@ValueSource(ints = {0, 1, 255, 256, 65535})
void readsUnsignedLocalVariableTableLengths(final int ranges) throws
IOException {
@@ -193,16 +231,16 @@ class TypeAnnotationEntryTest {
}
@ParameterizedTest
- @ValueSource(ints = {-1, 2, 15, 24, 63, 76, 127, 255, 256})
- void rejectsUndefinedConstructorTargets(final int target) {
- assertThrows(ClassFormatException.class, () -> new
TypeAnnotationEntry(target, new byte[0], new byte[0], null));
- }
-
- @ParameterizedTest
- @ValueSource(ints = {2, 15, 24, 63, 76, 127, 255})
- void rejectsUndefinedEncodedTargets(final int target) throws IOException {
- final byte[] bytes = encodedEntry(target, new byte[0], new byte[0]);
- assertThrows(ClassFormatException.class, () -> read(bytes, true));
+ @MethodSource("targetLayouts")
+ void rejectsIncorrectTargetInfoLengths(final int targetConstant, final int
tag, final byte[] info) {
+ final AnnotationEntry annotation = new AnnotationEntry(1,
constantPool(), true);
+ assertEquals(tag, targetConstant);
+ assertThrows(IllegalArgumentException.class,
+ () -> new TypeAnnotationEntry(targetConstant,
Arrays.copyOf(info, info.length + 1), new byte[0], annotation));
+ if (info.length > 0) {
+ assertThrows(IllegalArgumentException.class,
+ () -> new TypeAnnotationEntry(targetConstant,
Arrays.copyOf(info, info.length - 1), new byte[0], annotation));
+ }
}
@ParameterizedTest
@@ -214,65 +252,27 @@ class TypeAnnotationEntryTest {
}
}
- @Test
- void protectsTargetInfoAndPathFromCallerMutation() throws IOException {
- final byte[] info = {0, 2};
- final byte[] path = {3, 1};
- final TypeAnnotationEntry entry = new TypeAnnotationEntry(0x10, info,
path, new AnnotationEntry(1, constantPool(), true));
- info[0] = 99;
- path[0] = 99;
- path[1] = 99;
- final byte[] returnedInfo = entry.getTargetInfo();
- returnedInfo[0] = 88;
- assertArrayEquals(new byte[] {0, 2}, entry.getTargetInfo());
- assertEquals(3, entry.getTypePathKind(0));
- assertEquals(1, entry.getTypePathArgumentIndex(0));
- assertArrayEquals(encodedEntry(0x10, new byte[] {0, 2}, new byte[] {3,
1}), dump(entry));
+ @ParameterizedTest
+ @MethodSource("targetLayouts")
+ void rejectsTruncatedEntries(final int targetConstant, final int tag,
final byte[] info) throws IOException {
+ final byte[] bytes = encodedEntry(tag, info, TYPE_PATH);
+ for (int length = 0; length < bytes.length; length++) {
+ final byte[] truncated = Arrays.copyOf(bytes, length);
+ assertThrows(EOFException.class, () -> read(truncated, true),
"target " + targetConstant + ", length " + length);
+ }
}
@ParameterizedTest
- @ValueSource(booleans = {false, true})
- void deeplyCopiesNestedAnnotationValues(final boolean visible) throws
IOException {
- final ConstantPoolGen gen = new ConstantPoolGen();
- final int type = gen.addUtf8("Lexample/Annotation;");
- final int nestedType = gen.addUtf8("Lexample/Nested;");
- final int arrayName = gen.addUtf8("items");
- final int nestedName = gen.addUtf8("nested");
- final int valueName = gen.addUtf8("value");
- final int first = gen.addUtf8("first");
- final int second = gen.addUtf8("second");
- final ConstantPool pool = gen.getFinalConstantPool();
- final AnnotationEntry nested = new AnnotationEntry(nestedType, pool,
visible);
- nested.addElementNameValuePair(new ElementValuePair(valueName, new
SimpleElementValue(ElementValue.STRING, first, pool), pool));
- final AnnotationEntry annotation = new AnnotationEntry(type, pool,
visible);
- annotation.addElementNameValuePair(new ElementValuePair(arrayName, new
ArrayElementValue(ElementValue.ARRAY,
- new ElementValue[] {new
SimpleElementValue(ElementValue.STRING, first, pool)}, pool), pool));
- annotation.addElementNameValuePair(new ElementValuePair(nestedName,
new AnnotationElementValue(ElementValue.ANNOTATION, nested, pool), pool));
- final TypeAnnotationEntry original = new TypeAnnotationEntry(0x13, new
byte[0], TYPE_PATH, annotation);
- final byte[] expected = dump(original);
- final ConstantPool otherPool = pool.copy();
- final TypeAnnotationEntry copy = original.copy(otherPool);
- assertArrayEquals(expected, dump(copy));
- assertNotSame(original, copy);
- assertNotSame(annotation, copy.getAnnotationEntry());
- assertSame(otherPool, copy.getAnnotationEntry().getConstantPool());
- assertEquals(visible, copy.getAnnotationEntry().isRuntimeVisible());
- final ElementValuePair[] copiedPairs =
copy.getAnnotationEntry().getElementValuePairs();
- final ArrayElementValue array = (ArrayElementValue)
copiedPairs[0].getValue();
- final AnnotationEntry copiedNested = ((AnnotationElementValue)
copiedPairs[1].getValue()).getAnnotationEntry();
- assertNotSame(annotation.getElementValuePairs()[0], copiedPairs[0]);
- assertNotSame(((ArrayElementValue)
annotation.getElementValuePairs()[0].getValue()).getElementValuesArray(),
array.getElementValuesArray());
- assertNotSame(nested, copiedNested);
- assertSame(otherPool, array.getConstantPool());
- assertSame(otherPool,
array.getElementValuesArray()[0].getConstantPool());
- assertSame(otherPool, copiedNested.getConstantPool());
- assertSame(otherPool,
copiedNested.getElementValuePairs()[0].getValue().getConstantPool());
- assertEquals(visible, copiedNested.isRuntimeVisible());
- ((SimpleElementValue)
array.getElementValuesArray()[0]).setIndex(second);
- ((SimpleElementValue)
copiedNested.getElementValuePairs()[0].getValue()).setIndex(second);
- assertEquals("second",
array.getElementValuesArray()[0].stringifyValue());
- assertEquals("second",
copiedNested.getElementValuePairs()[0].getValue().stringifyValue());
- assertArrayEquals(expected, dump(original), "Mutating nested copied
values must not change the original");
+ @ValueSource(ints = {-1, 2, 15, 24, 63, 76, 127, 255, 256})
+ void rejectsUndefinedConstructorTargets(final int target) {
+ assertThrows(ClassFormatException.class, () -> new
TypeAnnotationEntry(target, new byte[0], new byte[0], null));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {2, 15, 24, 63, 76, 127, 255})
+ void rejectsUndefinedEncodedTargets(final int target) throws IOException {
+ final byte[] bytes = encodedEntry(target, new byte[0], new byte[0]);
+ assertThrows(ClassFormatException.class, () -> read(bytes, true));
}
@Test
diff --git a/src/test/java/org/apache/bcel/classfile/TypeAnnotationsTest.java
b/src/test/java/org/apache/bcel/classfile/TypeAnnotationsTest.java
index bda0a3ce..c1c857ac 100644
--- a/src/test/java/org/apache/bcel/classfile/TypeAnnotationsTest.java
+++ b/src/test/java/org/apache/bcel/classfile/TypeAnnotationsTest.java
@@ -55,19 +55,6 @@ import org.junit.jupiter.api.Test;
*/
class TypeAnnotationsTest {
- @Target(ElementType.TYPE_USE)
- @Retention(RetentionPolicy.RUNTIME)
- @interface TypeUse {
- }
-
- static class Outer {
- class Inner {
- }
-
- static class Nested {
- }
- }
-
static class AnnotatedTypes {
@TypeUse Outer.Inner outer;
Outer.@TypeUse Inner inner;
@@ -80,6 +67,19 @@ class TypeAnnotationsTest {
}
}
+ static class Outer {
+ class Inner {
+ }
+
+ static class Nested {
+ }
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface TypeUse {
+ }
+
private static final String CLASS_FILE =
"src/test/resources/typeannotations/TypeAnnotated.class";
private static JavaClass annotatedTypes() throws IOException {
@@ -90,101 +90,20 @@ class TypeAnnotationsTest {
}
}
- private static JavaClass parse() throws IOException {
- return new ClassParser(CLASS_FILE).parse();
- }
-
- private static TypeAnnotations typeAnnotationsOf(final FieldOrMethod
member, final byte tag) {
- return (TypeAnnotations) Stream.of(member.getAttributes()).filter(a ->
a.getTag() == tag).findFirst().orElse(null);
- }
-
- private static Method method(final JavaClass clazz, final String name) {
- return Stream.of(clazz.getMethods()).filter(m ->
m.getName().equals(name)).findFirst().orElseThrow(AssertionError::new);
- }
-
private static Field field(final JavaClass clazz, final String name) {
return Stream.of(clazz.getFields()).filter(f ->
f.getName().equals(name)).findFirst().orElseThrow(AssertionError::new);
}
- @Test
- void readsTheTypeAnnotationOfAField() throws IOException {
- final TypeAnnotations annotations = typeAnnotationsOf(field(parse(),
"field"), Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
- assertNotNull(annotations);
- assertTrue(annotations instanceof RuntimeVisibleTypeAnnotations);
- assertTrue(annotations.isRuntimeVisible());
- assertEquals(1, annotations.getNumTypeAnnotations());
- final TypeAnnotationEntry entry =
annotations.getTypeAnnotationEntries()[0];
- assertEquals(TypeAnnotationEntry.TARGET_FIELD, entry.getTargetType());
- assertEquals(0, entry.getTargetInfo().length);
- assertEquals(0, entry.getTypePathLength());
- assertEquals("Ltypeannotations/Visible;",
entry.getAnnotationEntry().getAnnotationType());
- }
-
- @Test
- void readsTheTypePathOfATypeArgument() throws IOException {
- final TypeAnnotations annotations = typeAnnotationsOf(field(parse(),
"items"), Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
- final TypeAnnotationEntry entry =
annotations.getTypeAnnotationEntries()[0];
- assertEquals(TypeAnnotationEntry.TARGET_FIELD, entry.getTargetType());
- assertEquals(1, entry.getTypePathLength());
- // a type argument of the parameterized type, the first one
- assertEquals(3, entry.getTypePathKind(0));
- assertEquals(0, entry.getTypePathArgumentIndex(0));
- }
-
- @Test
- void readsTheInvisibleAttribute() throws IOException {
- final Field hidden = field(parse(), "hidden");
- assertNull(typeAnnotationsOf(hidden,
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS));
- final TypeAnnotations annotations = typeAnnotationsOf(hidden,
Const.ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
- assertTrue(annotations instanceof RuntimeInvisibleTypeAnnotations);
- assertFalse(annotations.isRuntimeVisible());
- assertEquals("Ltypeannotations/Invisible;",
annotations.getTypeAnnotationEntries()[0].getAnnotationEntry().getAnnotationType());
- }
-
- @Test
- void readsTheTargetsOfAMethod() throws IOException {
- final TypeAnnotations annotations = typeAnnotationsOf(method(parse(),
"find"), Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
- final List<Integer> targets = new ArrayList<>();
- for (final TypeAnnotationEntry entry : annotations) {
- targets.add(entry.getTargetType());
- if (entry.getTargetType() ==
TypeAnnotationEntry.TARGET_METHOD_FORMAL_PARAMETER) {
- // the index of the parameter
- assertArrayEquals(new byte[] {0}, entry.getTargetInfo());
- }
- }
- assertTrue(targets.contains(TypeAnnotationEntry.TARGET_METHOD_RETURN));
-
assertTrue(targets.contains(TypeAnnotationEntry.TARGET_METHOD_FORMAL_PARAMETER));
+ private static Method method(final JavaClass clazz, final String name) {
+ return Stream.of(clazz.getMethods()).filter(m ->
m.getName().equals(name)).findFirst().orElseThrow(AssertionError::new);
}
- @Test
- void readsTheBoundOfATypeParameterOfTheClass() throws IOException {
- final JavaClass clazz = parse();
- final TypeAnnotations annotations = (TypeAnnotations)
Stream.of(clazz.getAttributes())
- .filter(a -> a.getTag() ==
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).findFirst().orElse(null);
- assertNotNull(annotations);
- final TypeAnnotationEntry entry =
annotations.getTypeAnnotationEntries()[0];
- assertEquals(TypeAnnotationEntry.TARGET_CLASS_TYPE_PARAMETER_BOUND,
entry.getTargetType());
- // the first type parameter, its first bound
- assertArrayEquals(new byte[] {0, 0}, entry.getTargetInfo());
+ private static JavaClass parse() throws IOException {
+ return new ClassParser(CLASS_FILE).parse();
}
- @Test
- void dumpsWhatItReads() throws IOException {
- final JavaClass clazz = parse();
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- try (DataOutputStream dos = new DataOutputStream(out)) {
- clazz.dump(dos);
- }
- assertArrayEquals(Files.readAllBytes(Paths.get(CLASS_FILE)),
out.toByteArray());
- final JavaClass reread = new ClassParser(new
ByteArrayInputStream(out.toByteArray()), CLASS_FILE).parse();
- for (final String name : new String[] {"field", "items", "hidden"}) {
- final Field original = field(clazz, name);
- final Field copy = field(reread, name);
- assertEquals(original.getAttributes().length,
copy.getAttributes().length);
- for (int i = 0; i < original.getAttributes().length; i++) {
- assertEquals(original.getAttributes()[i].toString(),
copy.getAttributes()[i].toString());
- }
- }
+ private static TypeAnnotations typeAnnotationsOf(final FieldOrMethod
member, final byte tag) {
+ return (TypeAnnotations) Stream.of(member.getAttributes()).filter(a ->
a.getTag() == tag).findFirst().orElse(null);
}
@Test
@@ -223,6 +142,58 @@ class TypeAnnotationsTest {
assertEquals(original.toString(), copy.toString());
}
+ @Test
+ void distinguishesInnerClassesFromStaticNestedClasses() throws IOException
{
+ final JavaClass clazz = annotatedTypes();
+ final TypeAnnotationEntry outer = typeAnnotationsOf(field(clazz,
"outer"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).getTypeAnnotationEntries()[0];
+ final TypeAnnotationEntry inner = typeAnnotationsOf(field(clazz,
"inner"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).getTypeAnnotationEntries()[0];
+ final TypeAnnotationEntry nested = typeAnnotationsOf(field(clazz,
"nested"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).getTypeAnnotationEntries()[0];
+ assertEquals(0, outer.getTypePathLength());
+ assertEquals(1, inner.getTypePathLength());
+ assertEquals(1, inner.getTypePathKind(0));
+ assertEquals(0, inner.getTypePathArgumentIndex(0));
+ assertEquals(0, nested.getTypePathLength());
+ }
+
+ @Test
+ void dumpsWhatItReads() throws IOException {
+ final JavaClass clazz = parse();
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try (DataOutputStream dos = new DataOutputStream(out)) {
+ clazz.dump(dos);
+ }
+ assertArrayEquals(Files.readAllBytes(Paths.get(CLASS_FILE)),
out.toByteArray());
+ final JavaClass reread = new ClassParser(new
ByteArrayInputStream(out.toByteArray()), CLASS_FILE).parse();
+ for (final String name : new String[] {"field", "items", "hidden"}) {
+ final Field original = field(clazz, name);
+ final Field copy = field(reread, name);
+ assertEquals(original.getAttributes().length,
copy.getAttributes().length);
+ for (int i = 0; i < original.getAttributes().length; i++) {
+ assertEquals(original.getAttributes()[i].toString(),
copy.getAttributes()[i].toString());
+ }
+ }
+ }
+
+ @Test
+ void followsTypeArgumentsWildcardBoundsAndArrayElements() throws
IOException {
+ final TypeAnnotationEntry entry =
typeAnnotationsOf(field(annotatedTypes(), "wildcard"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS)
+ .getTypeAnnotationEntries()[0];
+ assertEquals(3, entry.getTypePathLength());
+ assertEquals(3, entry.getTypePathKind(0));
+ assertEquals(2, entry.getTypePathKind(1));
+ assertEquals(0, entry.getTypePathKind(2));
+ for (int step = 0; step < 3; step++) {
+ assertEquals(0, entry.getTypePathArgumentIndex(step));
+ }
+ }
+
+ @Test
+ void isRepresented() throws IOException {
+ final JavaClass clazz = parse();
+ final StringRepresentation representation = new
StringRepresentation(typeAnnotationsOf(field(clazz, "field"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS));
+
assertTrue(representation.toString().contains("RuntimeVisibleTypeAnnotations"),
representation.toString());
+ }
+
@Test
void isVisitedWithItsAnnotations() throws IOException {
final JavaClass clazz = parse();
@@ -250,10 +221,25 @@ class TypeAnnotationsTest {
}
@Test
- void isRepresented() throws IOException {
+ void readsTheBoundOfATypeParameterOfTheClass() throws IOException {
final JavaClass clazz = parse();
- final StringRepresentation representation = new
StringRepresentation(typeAnnotationsOf(field(clazz, "field"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS));
-
assertTrue(representation.toString().contains("RuntimeVisibleTypeAnnotations"),
representation.toString());
+ final TypeAnnotations annotations = (TypeAnnotations)
Stream.of(clazz.getAttributes())
+ .filter(a -> a.getTag() ==
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).findFirst().orElse(null);
+ assertNotNull(annotations);
+ final TypeAnnotationEntry entry =
annotations.getTypeAnnotationEntries()[0];
+ assertEquals(TypeAnnotationEntry.TARGET_CLASS_TYPE_PARAMETER_BOUND,
entry.getTargetType());
+ // the first type parameter, its first bound
+ assertArrayEquals(new byte[] {0, 0}, entry.getTargetInfo());
+ }
+
+ @Test
+ void readsTheInvisibleAttribute() throws IOException {
+ final Field hidden = field(parse(), "hidden");
+ assertNull(typeAnnotationsOf(hidden,
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS));
+ final TypeAnnotations annotations = typeAnnotationsOf(hidden,
Const.ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
+ assertTrue(annotations instanceof RuntimeInvisibleTypeAnnotations);
+ assertFalse(annotations.isRuntimeVisible());
+ assertEquals("Ltypeannotations/Invisible;",
annotations.getTypeAnnotationEntries()[0].getAnnotationEntry().getAnnotationType());
}
@Test
@@ -283,10 +269,43 @@ class TypeAnnotationsTest {
}
@Test
- void rejectsAnUndefinedTargetType() throws IOException {
- final ConstantPool constantPool = parse().getConstantPool();
- final DataInputStream input = new DataInputStream(new
ByteArrayInputStream(new byte[] {0x7F, 0, 0, 0}));
- assertThrows(ClassFormatException.class, () ->
TypeAnnotationEntry.read(input, constantPool, true));
+ void readsTheTargetsOfAMethod() throws IOException {
+ final TypeAnnotations annotations = typeAnnotationsOf(method(parse(),
"find"), Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+ final List<Integer> targets = new ArrayList<>();
+ for (final TypeAnnotationEntry entry : annotations) {
+ targets.add(entry.getTargetType());
+ if (entry.getTargetType() ==
TypeAnnotationEntry.TARGET_METHOD_FORMAL_PARAMETER) {
+ // the index of the parameter
+ assertArrayEquals(new byte[] {0}, entry.getTargetInfo());
+ }
+ }
+ assertTrue(targets.contains(TypeAnnotationEntry.TARGET_METHOD_RETURN));
+
assertTrue(targets.contains(TypeAnnotationEntry.TARGET_METHOD_FORMAL_PARAMETER));
+ }
+
+ @Test
+ void readsTheTypeAnnotationOfAField() throws IOException {
+ final TypeAnnotations annotations = typeAnnotationsOf(field(parse(),
"field"), Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+ assertNotNull(annotations);
+ assertTrue(annotations instanceof RuntimeVisibleTypeAnnotations);
+ assertTrue(annotations.isRuntimeVisible());
+ assertEquals(1, annotations.getNumTypeAnnotations());
+ final TypeAnnotationEntry entry =
annotations.getTypeAnnotationEntries()[0];
+ assertEquals(TypeAnnotationEntry.TARGET_FIELD, entry.getTargetType());
+ assertEquals(0, entry.getTargetInfo().length);
+ assertEquals(0, entry.getTypePathLength());
+ assertEquals("Ltypeannotations/Visible;",
entry.getAnnotationEntry().getAnnotationType());
+ }
+
+ @Test
+ void readsTheTypePathOfATypeArgument() throws IOException {
+ final TypeAnnotations annotations = typeAnnotationsOf(field(parse(),
"items"), Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+ final TypeAnnotationEntry entry =
annotations.getTypeAnnotationEntries()[0];
+ assertEquals(TypeAnnotationEntry.TARGET_FIELD, entry.getTargetType());
+ assertEquals(1, entry.getTypePathLength());
+ // a type argument of the parameterized type, the first one
+ assertEquals(3, entry.getTypePathKind(0));
+ assertEquals(0, entry.getTypePathArgumentIndex(0));
}
@Test
@@ -301,6 +320,13 @@ class TypeAnnotationsTest {
assertEquals(255, new
TypeAnnotationEntry(TypeAnnotationEntry.TARGET_FIELD, new byte[0], new byte[255
* 2], annotation).getTypePathLength());
}
+ @Test
+ void rejectsAnUndefinedTargetType() throws IOException {
+ final ConstantPool constantPool = parse().getConstantPool();
+ final DataInputStream input = new DataInputStream(new
ByteArrayInputStream(new byte[] {0x7F, 0, 0, 0}));
+ assertThrows(ClassFormatException.class, () ->
TypeAnnotationEntry.read(input, constantPool, true));
+ }
+
@Test
void rejectsATargetInfoOfTheWrongSize() throws IOException {
final AnnotationEntry annotation = typeAnnotationsOf(field(parse(),
"field"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).getTypeAnnotationEntries()[0]
@@ -318,32 +344,6 @@ class TypeAnnotationsTest {
assertEquals(2, new
TypeAnnotationEntry(TypeAnnotationEntry.TARGET_LOCAL_VARIABLE, new byte[2], new
byte[0], annotation).getTargetInfo().length);
}
- @Test
- void distinguishesInnerClassesFromStaticNestedClasses() throws IOException
{
- final JavaClass clazz = annotatedTypes();
- final TypeAnnotationEntry outer = typeAnnotationsOf(field(clazz,
"outer"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).getTypeAnnotationEntries()[0];
- final TypeAnnotationEntry inner = typeAnnotationsOf(field(clazz,
"inner"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).getTypeAnnotationEntries()[0];
- final TypeAnnotationEntry nested = typeAnnotationsOf(field(clazz,
"nested"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS).getTypeAnnotationEntries()[0];
- assertEquals(0, outer.getTypePathLength());
- assertEquals(1, inner.getTypePathLength());
- assertEquals(1, inner.getTypePathKind(0));
- assertEquals(0, inner.getTypePathArgumentIndex(0));
- assertEquals(0, nested.getTypePathLength());
- }
-
- @Test
- void followsTypeArgumentsWildcardBoundsAndArrayElements() throws
IOException {
- final TypeAnnotationEntry entry =
typeAnnotationsOf(field(annotatedTypes(), "wildcard"),
Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS)
- .getTypeAnnotationEntries()[0];
- assertEquals(3, entry.getTypePathLength());
- assertEquals(3, entry.getTypePathKind(0));
- assertEquals(2, entry.getTypePathKind(1));
- assertEquals(0, entry.getTypePathKind(2));
- for (int step = 0; step < 3; step++) {
- assertEquals(0, entry.getTypePathArgumentIndex(step));
- }
- }
-
@Test
void visitsCompilerGeneratedCodeAnnotations() throws IOException {
final JavaClass clazz = annotatedTypes();