mockit.asm.annotations.AnnotationReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for automated developer testing.
It contains mocking/faking APIs and a code coverage tool, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
package mockit.asm.annotations;
import java.lang.reflect.*;
import javax.annotation.*;
import mockit.asm.types.*;
import mockit.asm.util.*;
public final class AnnotationReader extends BytecodeReader
{
public AnnotationReader(@Nonnull BytecodeReader br) { super(br); }
/**
* Reads the values of a named annotation and makes the given visitor visit them.
*
* @param startingCodeIndex the start offset in {@link #code} of the values to be read (including the unsigned short that gives the
* number of values)
* @param av the visitor that must visit the values
* @return the end offset of the annotation values
*/
@Nonnegative
public int readNamedAnnotationValues(@Nonnegative int startingCodeIndex, @Nullable AnnotationVisitor av) {
codeIndex = startingCodeIndex;
readAnnotationValues(true, av);
return codeIndex;
}
private void readAnnotationValues(boolean named, @Nullable AnnotationVisitor av) {
int valueCount = readUnsignedShort();
readAnnotationValues(valueCount, named, av);
}
private void readAnnotationValues(@Nonnegative int valueCount, boolean named, @Nullable AnnotationVisitor av) {
while (valueCount > 0) {
String name = named ? readNonnullUTF8() : null;
readAnnotationValue(name, av);
valueCount--;
}
if (av != null) {
av.visitEnd();
}
}
private void readAnnotationValue(@Nullable String name, @Nullable AnnotationVisitor av) {
int typeCode = readUnsignedByte();
if (av == null) {
readAnnotationValue(typeCode);
}
else {
Object value = readAnnotationValueIfPrimitiveOrString(typeCode);
if (value != null) {
av.visit(name, value);
}
else {
//noinspection SwitchStatementWithoutDefaultBranch
switch (typeCode) {
case 'e': readEnumConstValue(name, av); break; // enum_const_value
case 'c': readClassInfo(name, av); break; // class_info
case '@': readNestedAnnotation(name, av); break; // annotation_value
case '[': readArrayValue(name, av); // array_value
}
}
}
}
private void readAnnotationValue(@Nonnegative int typeCode) {
switch (typeCode) {
case 'e': codeIndex += 4; break; // enum_const_value
case '@': codeIndex += 2; readAnnotationValues(true, null); break; // annotation_value
case '[': readAnnotationValues(false, null); break;
default: codeIndex += 2;
}
}
@Nullable @SuppressWarnings({"NumericCastThatLosesPrecision", "SwitchStatementWithoutDefaultBranch"})
private Object readAnnotationValueIfPrimitiveOrString(@Nonnegative int typeCode) {
switch (typeCode) {
case 'I': case 'J': case 'F': case 'D': return readConstItem(); // CONSTANT_Integer/Long/Float/Double
case 'B': return (byte) readValueOfOneOrTwoBytes(); // CONSTANT_Byte
case 'Z': return readValueOfOneOrTwoBytes() != 0; // CONSTANT_Boolean
case 'S': return (short) readValueOfOneOrTwoBytes(); // CONSTANT_Short
case 'C': return (char) readValueOfOneOrTwoBytes(); // CONSTANT_Char
case 's': return readNonnullUTF8(); // CONSTANT_Utf8
}
return null;
}
private int readValueOfOneOrTwoBytes() {
int itemIndex = readUnsignedShort();
int valueCodeIndex = items[itemIndex];
return readInt(valueCodeIndex);
}
private void readEnumConstValue(@Nullable String name, @Nonnull AnnotationVisitor av) {
String enumDesc = readNonnullUTF8();
String enumValue = readNonnullUTF8();
av.visitEnum(name, enumDesc, enumValue);
}
private void readClassInfo(@Nullable String name, @Nonnull AnnotationVisitor av) {
String typeDesc = readNonnullUTF8();
JavaType value = JavaType.getType(typeDesc);
av.visit(name, value);
}
private void readNestedAnnotation(@Nullable String name, @Nonnull AnnotationVisitor av) {
String desc = readNonnullUTF8();
AnnotationVisitor nestedVisitor = av.visitAnnotation(name, desc);
readAnnotationValues(true, nestedVisitor);
}
private void readArrayValue(@Nullable String name, @Nonnull AnnotationVisitor av) {
int valueCount = readUnsignedShort();
if (valueCount == 0) {
AnnotationVisitor arrayVisitor = av.visitArray(name);
arrayVisitor.visitEnd();
return;
}
int typeCode = readUnsignedByte();
PrimitiveType primitiveElementType = PrimitiveType.getPrimitiveType(typeCode);
if (primitiveElementType == null) {
AnnotationVisitor arrayVisitor = av.visitArray(name);
codeIndex--;
readAnnotationValues(valueCount, false, arrayVisitor);
return;
}
Class> elementType = primitiveElementType.getType();
Object array = Array.newInstance(elementType, valueCount);
fillArrayElements(valueCount, typeCode, array);
av.visit(name, array);
codeIndex--;
}
private void fillArrayElements(@Nonnegative int length, @Nonnegative int typeCode, @Nonnull Object array) {
for (int i = 0; i < length; i++) {
int itemIndex = readUnsignedShort();
int index = items[itemIndex];
Object value = getArrayElementValue(typeCode, index);
Array.set(array, i, value);
codeIndex++;
}
}
@Nonnull
private Object getArrayElementValue(@Nonnegative int typeCode, @Nonnegative int valueCodeIndex) {
switch (typeCode) {
case 'Z': return readBoolean(valueCodeIndex);
case 'C': return readChar(valueCodeIndex);
case 'B': return readUnsignedByte(valueCodeIndex);
case 'S': return readShort(valueCodeIndex);
case 'F': return readFloat(valueCodeIndex);
case 'D': return readDouble(valueCodeIndex);
case 'J': return readLong(valueCodeIndex);
default: return readInt(valueCodeIndex);
}
}
}