mockit.asm.AnnotationWriter 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;
import java.lang.reflect.*;
import javax.annotation.*;
/**
* An {@link AnnotationVisitor} that generates annotations in bytecode form.
*/
final class AnnotationWriter extends AnnotationVisitor
{
/**
* The constant pool to which this annotation must be added.
*/
@Nonnull private final ConstantPoolGeneration cp;
/**
* The number of values in this annotation.
*/
@Nonnegative private int size;
/**
* true if values are named, false otherwise.
* Annotation writers used for annotation default and annotation arrays use unnamed values.
*/
private final boolean named;
/**
* The annotation values in bytecode form. This byte vector only contains the values themselves, i.e. the number of
* values must be stored as an unsigned short just before these bytes.
*/
@Nonnull private final ByteVector bv;
/**
* Where the number of values of this annotation must be stored in {@link #bv}.
*/
@Nonnegative private final int offset;
AnnotationWriter(@Nonnull ConstantPoolGeneration cp, @Nonnull String typeDesc) {
this.cp = cp;
named = true;
bv = new ByteVector();
bv.putShort(cp.newUTF8(typeDesc));
bv.putShort(0); // reserve space for value count
offset = 2;
}
private AnnotationWriter(@Nonnull AnnotationWriter parent, boolean named) {
cp = parent.cp;
this.named = named;
bv = parent.bv;
offset = bv.length - 2;
}
@Nonnegative @Override
protected int getByteLength() { return bv.length; }
@Override
public void visit(@Nullable String name, @Nonnull Object value) {
putName(name);
if (value instanceof String) {
putString('s', (String) value);
}
else if (putValueWhenPrimitive(value)) {
// OK
}
else if (value instanceof JavaType) {
putType((JavaType) value);
}
else {
putElementValuesWhenArray(value);
}
}
private void putName(@Nullable String name) {
size++;
if (named) {
//noinspection ConstantConditions
putString(name);
}
}
private boolean putValueWhenPrimitive(@Nonnull Object value) {
if (value instanceof Boolean) {
putBoolean((Boolean) value);
}
else if (value instanceof Integer) {
putInteger('I', (Integer) value);
}
else if (value instanceof Double) {
putDouble((Double) value);
}
else if (value instanceof Float) {
putFloat((Float) value);
}
else if (value instanceof Long) {
putLong((Long) value);
}
else if (value instanceof Byte) {
putInteger('B', (Byte) value);
}
else if (value instanceof Character) {
putInteger('C', (Character) value);
}
else if (value instanceof Short) {
putInteger('S', (Short) value);
}
else {
return false;
}
return true;
}
private void putItem(int typeCode, @Nonnull Item item) {
bv.put12(typeCode, item.index);
}
private void putBoolean(boolean value) {
putInteger('Z', value ? 1 : 0);
}
private void putInteger(int typeCode, int value) {
Item item = cp.newInteger(value);
putItem(typeCode, item);
}
private void putDouble(double value) {
Item item = cp.newDouble(value);
putItem('D', item);
}
private void putFloat(float value) {
Item item = cp.newFloat(value);
putItem('F', item);
}
private void putLong(long value) {
Item item = cp.newLong(value);
putItem('J', item);
}
private void putType(@Nonnull JavaType type) {
String typeDescriptor = type.getDescriptor();
putString('c', typeDescriptor);
}
private void putString(int b, @Nonnull String value) {
int itemIndex = cp.newUTF8(value);
bv.put12(b, itemIndex);
}
private void putString(@Nonnull String value) {
int itemIndex = cp.newUTF8(value);
bv.putShort(itemIndex);
}
private void putArrayLength(@Nonnegative int length) {
bv.put12('[', length);
}
private void putElementValuesWhenArray(@Nonnull Object value) {
if (value instanceof byte[]) {
putArrayElementValues('B', value);
}
else if (value instanceof boolean[]) {
putArrayElementValues('Z', value);
}
else if (value instanceof short[]) {
putArrayElementValues('S', value);
}
else if (value instanceof char[]) {
putArrayElementValues('C', value);
}
else if (value instanceof int[]) {
putArrayElementValues('I', value);
}
else if (value instanceof long[]) {
putArrayElementValues('J', value);
}
else if (value instanceof float[]) {
putArrayElementValues('F', value);
}
else if (value instanceof double[]) {
putArrayElementValues('D', value);
}
}
private void putArrayElementValues(char elementType, @Nonnull Object array) {
int length = Array.getLength(array);
putArrayLength(length);
for (int i = 0; i < length; i++) {
if (elementType == 'J') {
long value = Array.getLong(array, i);
putLong(value);
}
else if (elementType == 'F') {
float value = Array.getFloat(array, i);
putFloat(value);
}
else if (elementType == 'D') {
double value = Array.getDouble(array, i);
putDouble(value);
}
else if (elementType == 'Z') {
boolean value = Array.getBoolean(array, i);
putBoolean(value);
}
else {
int value = Array.getInt(array, i);
putInteger(elementType, value);
}
}
}
@Override
public void visitEnum(@Nullable String name, @Nonnull String desc, @Nonnull String value) {
putName(name);
putString('e', desc);
putString(value);
}
@Nonnull @Override
public AnnotationVisitor visitAnnotation(@Nullable String name, @Nonnull String desc) {
putName(name);
// Write tag and type, and reserve space for value count.
putString('@', desc);
bv.putShort(0);
return new AnnotationWriter(this, true);
}
@Nonnull @Override
public AnnotationVisitor visitArray(@Nullable String name) {
putName(name);
// Write tag, and reserve space for array size.
putArrayLength(0);
return new AnnotationWriter(this, false);
}
@Override
public void visitEnd() {
byte[] data = bv.data;
data[offset] = (byte) (size >>> 8);
data[offset + 1] = (byte) size;
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Puts the annotations of this annotation writer list into the given byte vector.
*
* @param out where the annotations must be put.
*/
void put(@Nonnull ByteVector out) {
AnnotationWriter aw = this;
AnnotationWriter last = null;
int n = 0;
int size = 2;
while (aw != null) {
n++;
size += aw.getByteLength();
aw.prev = last;
last = aw;
aw = aw.next;
}
out.putInt(size);
out.putShort(n);
putFromLastToFirst(out, last);
}
private static void putFromLastToFirst(@Nonnull ByteVector out, @Nullable AnnotationWriter aw) {
while (aw != null) {
out.putByteVector(aw.bv);
aw = aw.prev;
}
}
/**
* Puts the given annotation lists into the given byte vector.
*
* @param out where the annotations must be put.
* @param anns an array of annotation writer lists.
*/
static void put(@Nonnull ByteVector out, @Nonnull AnnotationWriter[] anns) {
putNumberAndSizeOfAnnotations(out, anns);
for (AnnotationWriter ann : anns) {
AnnotationWriter last = putNumberOfAnnotations(out, ann);
putFromLastToFirst(out, last);
}
}
private static void putNumberAndSizeOfAnnotations(@Nonnull ByteVector out, @Nonnull AnnotationWriter[] anns) {
int numAnns = anns.length;
int size = 1 + 2 * numAnns;
for (AnnotationWriter aw : anns) {
if (aw != null) {
size += aw.getSize();
}
}
out.putInt(size).putByte(numAnns);
}
@Nullable
private static AnnotationWriter putNumberOfAnnotations(@Nonnull ByteVector out, @Nullable AnnotationWriter aw) {
AnnotationWriter last = null;
int n = 0;
while (aw != null) {
n++;
aw.prev = last;
last = aw;
aw = aw.next;
}
out.putShort(n);
return last;
}
}