mockit.external.asm.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 APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external
APIs; JUnit (4 & 5) and TestNG test runners are supported.
It also contains an advanced code coverage tool.
package mockit.external.asm;
import java.lang.reflect.*;
import javax.annotation.*;
final class AnnotationReader extends BytecodeReader
{
AnnotationReader(@Nonnull BytecodeReader br) { super(br); }
/**
* Reads the values of an annotation and makes the given visitor visit them.
*
* @param v the start offset in {@link #b b} of the values to be read
* (including the unsigned short that gives the number of values).
* @param buf buffer to be used to call {@link #readUTF8 readUTF8},
* {@link #readClass(int, char[]) readClass} or {@link #readConst readConst}.
* @param named if the annotation values are named or not.
* @param av the visitor that must visit the values.
* @return the end offset of the annotation values.
*/
@Nonnegative
int readAnnotationValues(@Nonnegative int v, @Nonnull char[] buf, boolean named, @Nullable AnnotationVisitor av) {
int i = readUnsignedShort(v);
v += 2;
if (named) {
for (; i > 0; i--) {
String name = readUTF8(v, buf);
v = readAnnotationValue(v + 2, buf, name, av);
}
}
else {
for (; i > 0; i--) {
v = readAnnotationValue(v, buf, null, av);
}
}
if (av != null) {
av.visitEnd();
}
return v;
}
/**
* Reads a value of an annotation and makes the given visitor visit it.
*
* @param v the start offset in {@link #b} of the value to be read
* (not including the value name constant pool index).
* @param buf buffer to be used to call {@link #readUTF8 readUTF8},
* {@link #readClass(int, char[]) readClass} or {@link #readConst readConst}.
* @param name the name of the value to be read.
* @param av the visitor that must visit the value.
* @return the end offset of the annotation value.
*/
@Nonnegative
int readAnnotationValue(
@Nonnegative int v, @Nonnull char[] buf, @Nullable String name, @Nullable AnnotationVisitor av
) {
if (av == null) {
return readAnnotationValue(v, buf);
}
int typeCode = b[v++] & 0xFF;
Object value;
switch (typeCode) {
case 'I': // pointer to CONSTANT_Integer
case 'J': // pointer to CONSTANT_Long
case 'F': // pointer to CONSTANT_Float
case 'D': // pointer to CONSTANT_Double
value = readConst(readUnsignedShort(v), buf);
av.visit(name, value);
v += 2;
break;
case 'B': // pointer to CONSTANT_Byte
value = (byte) readInt(items[readUnsignedShort(v)]);
av.visit(name, value);
v += 2;
break;
case 'Z': // pointer to CONSTANT_Boolean
value = readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE : Boolean.TRUE;
av.visit(name, value);
v += 2;
break;
case 'S': // pointer to CONSTANT_Short
value = (short) readInt(items[readUnsignedShort(v)]);
av.visit(name, value);
v += 2;
break;
case 'C': // pointer to CONSTANT_Char
value = (char) readInt(items[readUnsignedShort(v)]);
av.visit(name, value);
v += 2;
break;
case 's': // pointer to CONSTANT_Utf8
value = readUTF8(v, buf);
//noinspection ConstantConditions
av.visit(name, value);
v += 2;
break;
case 'e': // enum_const_value
String enumDesc = readUTF8(v, buf);
String enumValue = readUTF8(v + 2, buf);
//noinspection ConstantConditions
av.visitEnum(name, enumDesc, enumValue);
v += 4;
break;
case 'c': // class_info
String typeDesc = readUTF8(v, buf);
//noinspection ConstantConditions
value = JavaType.getType(typeDesc);
av.visit(name, value);
v += 2;
break;
case '@': // annotation_value
String desc = readUTF8(v, buf);
//noinspection ConstantConditions
AnnotationVisitor nestedVisitor = av.visitAnnotation(name, desc);
v = readAnnotationValues(v + 2, buf, true, nestedVisitor);
break;
case '[': // array_value
int size = readUnsignedShort(v);
v += 2;
if (size == 0) {
AnnotationVisitor arrayVisitor = av.visitArray(name);
return readAnnotationValues(v - 2, buf, false, arrayVisitor);
}
v = readAnnotationArrayValue(v, buf, name, av, size);
}
return v;
}
@Nonnegative
private int readAnnotationValue(@Nonnegative int v, @Nonnull char[] buf) {
int typeCode = b[v] & 0xFF;
switch (typeCode) {
case 'e': // enum_const_value
return v + 5;
case '@': // annotation_value
return readAnnotationValues(v + 3, buf, true, null);
case '[': // array_value
return readAnnotationValues(v + 1, buf, false, null);
default:
return v + 3;
}
}
@Nonnegative
private int readAnnotationArrayValue(
@Nonnegative int v, @Nonnull char[] buf, @Nullable String name, @Nonnull AnnotationVisitor av,
@Nonnegative int size
) {
int typeCode = b[v++] & 0xFF;
if ("BZSCIJFD".indexOf(typeCode) < 0) {
AnnotationVisitor arrayVisitor = av.visitArray(name);
return readAnnotationValues(v - 3, buf, false, arrayVisitor);
}
Class elementType = PrimitiveType.getType(typeCode);
Object array = Array.newInstance(elementType, size);
for (int i = 0; i < size; i++) {
int index = items[readUnsignedShort(v)];
Object value;
switch (typeCode) {
case 'B':
value = (byte) readInt(index);
break;
case 'Z':
value = readInt(index) != 0;
break;
case 'S':
value = (short) readInt(index);
break;
case 'C':
value = (char) readInt(index);
break;
case 'I':
value = readInt(index);
break;
case 'J':
value = readLong(index);
break;
case 'F':
int floatBits = readInt(index);
value = Float.intBitsToFloat(floatBits);
break;
default: // 'D'
long doubleBits = readLong(index);
value = Double.longBitsToDouble(doubleBits);
}
Array.set(array, i, value);
v += 3;
}
av.visit(name, array);
v--;
return v;
}
}