Skip to content

Commit

Permalink
Added possibility to map field value dynamically and added tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Nov 2, 2016
1 parent 63f7787 commit d6d06d7
Show file tree
Hide file tree
Showing 3 changed files with 320 additions and 18 deletions.
144 changes: 126 additions & 18 deletions byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java
Expand Up @@ -28,6 +28,7 @@
import java.io.*; import java.io.*;
import java.lang.annotation.*; import java.lang.annotation.*;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;


Expand Down Expand Up @@ -1911,32 +1912,48 @@ protected static void loadInteger(MethodVisitor methodVisitor, int value) {
* *
* @param methodVisitor the method visitor for which to load the value. * @param methodVisitor the method visitor for which to load the value.
* @param offset The offset of the primitive value. * @param offset The offset of the primitive value.
* @return The additional padding required on the operand stack.
*/ */
protected void loadBoxed(MethodVisitor methodVisitor, int offset) { protected int loadBoxed(MethodVisitor methodVisitor, int offset) {
methodVisitor.visitVarInsn(load, offset); methodVisitor.visitVarInsn(load, offset);
return box(methodVisitor);
}

/**
* Boxes the current value on top of the operand stack.
*
* @param methodVisitor The method visitor to apply the boxing to.
* @return The additional padding required on the operand stack.
*/
protected int box(MethodVisitor methodVisitor) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, owner, VALUE_OF, boxingDescriptor, false); methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, owner, VALUE_OF, boxingDescriptor, false);
return stackSize.getSize() - 1;
} }


/** /**
* Stores the value as a unboxed version onto the stack. * Stores the value as a unboxed version onto the stack.
* *
* @param methodVisitor the method visitor for which to store the value. * @param methodVisitor the method visitor for which to store the value.
* @param offset The offset of the primitive value. * @param offset The offset of the primitive value.
* @return The additional padding required on the operand stack.
*/ */
protected void storeUnboxed(MethodVisitor methodVisitor, int offset) { protected int storeUnboxed(MethodVisitor methodVisitor, int offset) {
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, owner); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, owner);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, unboxingMethod, unboxingDescriptor, false); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, unboxingMethod, unboxingDescriptor, false);
methodVisitor.visitVarInsn(store, offset); methodVisitor.visitVarInsn(store, offset);
return stackSize.getSize() - 1;
} }


/** /**
* Pushes the represented default value as a boxed value onto the operand stack. * Pushes the represented default value as a boxed value onto the operand stack.
* *
* @param methodVisitor The method visitor to apply the changes to. * @param methodVisitor The method visitor to apply the changes to.
* @return The additional padding required on the operand stack.
*/ */
protected void pushBoxedDefault(MethodVisitor methodVisitor) { protected int pushBoxedDefault(MethodVisitor methodVisitor) {
methodVisitor.visitInsn(defaultValue); methodVisitor.visitInsn(defaultValue);
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, owner, VALUE_OF, boxingDescriptor, false); methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, owner, VALUE_OF, boxingDescriptor, false);
return stackSize.getSize() - 1;
} }


/** /**
Expand Down Expand Up @@ -2050,8 +2067,7 @@ protected static Target of(TypeDefinition typeDefinition) {
public int resolveAccess(MethodVisitor methodVisitor, int opcode) { public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
switch (opcode) { switch (opcode) {
case Opcodes.ALOAD: case Opcodes.ALOAD:
primitiveDispatcher.pushBoxedDefault(methodVisitor); return primitiveDispatcher.pushBoxedDefault(methodVisitor);
return primitiveDispatcher.getStackSize().getSize() - 1;
case Opcodes.ASTORE: case Opcodes.ASTORE:
methodVisitor.visitInsn(Opcodes.POP); methodVisitor.visitInsn(Opcodes.POP);
return NO_PADDING; return NO_PADDING;
Expand Down Expand Up @@ -2490,6 +2506,66 @@ public String toString() {
"}"; "}";
} }
} }

/**
* A target for reading a field where the final value is boxed after reading.
*/
protected static class ReadBoxed extends ForField {

/**
* Creates a new field mapping for a field that is readable and gets boxed.
*
* @param fieldDescription The field which is mapped by this target mapping.
*/
protected ReadBoxed(FieldDescription.InDefinedShape fieldDescription) {
super(fieldDescription);
}

/**
* Resolves a read-only target for the field description.
*
* @param instrumentedType The instrumented type.
* @param fieldDescription The field which is mapped by this target mapping.
* @return A target for reading a field where the value is boxed, if required.
*/
protected static Target of(TypeDescription instrumentedType, FieldDescription.InDefinedShape fieldDescription) {
if (!fieldDescription.isStatic() && !instrumentedType.isAssignableTo(fieldDescription.getDeclaringType())) {
throw new IllegalStateException("Cannot access " + fieldDescription + " from " + instrumentedType);
} else if (!fieldDescription.isVisibleTo(instrumentedType)) {
throw new IllegalStateException(fieldDescription + " is not visible from " + instrumentedType);
}
return fieldDescription.getType().isPrimitive()
? new ReadBoxed(fieldDescription)
: new ReadOnly(fieldDescription);
}

@Override
public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
return super.resolveAccess(methodVisitor, opcode) + PrimitiveDispatcher.of(fieldDescription.getType()).box(methodVisitor);
}

@Override
protected int onWriteSingle(MethodVisitor methodVisitor) {
throw new IllegalStateException("Cannot write to read-only field " + fieldDescription);
}

@Override
protected int onWriteDouble(MethodVisitor methodVisitor) {
throw new IllegalStateException("Cannot write to read-only field " + fieldDescription);
}

@Override
public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
throw new IllegalStateException("Cannot write to read-only field " + fieldDescription);
}

@Override
public String toString() {
return "Advice.Dispatcher.OffsetMapping.Target.ForField.ReadBoxed{" +
"fieldDescription=" + fieldDescription +
"}";
}
}
} }


/** /**
Expand Down Expand Up @@ -2667,11 +2743,9 @@ protected ForBoxedArgument(int offset, PrimitiveDispatcher primitiveDispatcher)
public int resolveAccess(MethodVisitor methodVisitor, int opcode) { public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
switch (opcode) { switch (opcode) {
case Opcodes.ALOAD: case Opcodes.ALOAD:
primitiveDispatcher.loadBoxed(methodVisitor, offset); return primitiveDispatcher.loadBoxed(methodVisitor, offset);
return primitiveDispatcher.getStackSize().getSize() - 1;
case Opcodes.ASTORE: case Opcodes.ASTORE:
onStore(methodVisitor); return onStore(methodVisitor);
return NO_PADDING;
default: default:
throw new IllegalStateException("Unexpected opcode: " + opcode); throw new IllegalStateException("Unexpected opcode: " + opcode);
} }
Expand All @@ -2681,8 +2755,9 @@ public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
* Handles writing the boxed value if applicable. * Handles writing the boxed value if applicable.
* *
* @param methodVisitor The method visitor for which to apply the writing. * @param methodVisitor The method visitor for which to apply the writing.
* @return The additional required stack size.
*/ */
protected abstract void onStore(MethodVisitor methodVisitor); protected abstract int onStore(MethodVisitor methodVisitor);


@Override @Override
public int resolveIncrement(MethodVisitor methodVisitor, int increment) { public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
Expand Down Expand Up @@ -2735,7 +2810,7 @@ protected static Target of(int offset, TypeDefinition type) {
} }


@Override @Override
protected void onStore(MethodVisitor methodVisitor) { protected int onStore(MethodVisitor methodVisitor) {
throw new IllegalStateException("Cannot write to read-only boxed parameter"); throw new IllegalStateException("Cannot write to read-only boxed parameter");
} }


Expand Down Expand Up @@ -2775,8 +2850,8 @@ protected static Target of(int offset, TypeDefinition type) {
} }


@Override @Override
protected void onStore(MethodVisitor methodVisitor) { protected int onStore(MethodVisitor methodVisitor) {
primitiveDispatcher.storeUnboxed(methodVisitor, offset); return primitiveDispatcher.storeUnboxed(methodVisitor, offset);
} }


@Override @Override
Expand Down Expand Up @@ -5013,7 +5088,7 @@ public Target resolve(TypeDescription instrumentedType, MethodDescription instru
throw new IllegalStateException("Cannot map null to primitive type of " + target); throw new IllegalStateException("Cannot map null to primitive type of " + target);
} }
return Target.ForNullConstant.READ_ONLY; return Target.ForNullConstant.READ_ONLY;
} else if ((target.getType().asErasure().isAssignableFrom(String.class) && value instanceof String) } else if ((value instanceof String && target.getType().asErasure().isAssignableFrom(String.class))
|| (target.getType().isPrimitive() && target.getType().asErasure().isInstanceOrWrapper(value))) { || (target.getType().isPrimitive() && target.getType().asErasure().isInstanceOrWrapper(value))) {
if (value instanceof Boolean) { if (value instanceof Boolean) {
value = (Boolean) value ? 1 : 0; value = (Boolean) value ? 1 : 0;
Expand All @@ -5025,11 +5100,15 @@ public Target resolve(TypeDescription instrumentedType, MethodDescription instru
value = (int) ((Character) value).charValue(); value = (int) ((Character) value).charValue();
} }
return new Target.ForConstantPoolValue(value); return new Target.ForConstantPoolValue(value);
} else if (target.getType().asErasure().isAssignableFrom(Class.class) && value instanceof Class) { } else if (value instanceof Class && target.getType().asErasure().isAssignableFrom(Class.class)) {
return new Target.ForConstantPoolValue(Type.getType((Class<?>) value)); return new Target.ForConstantPoolValue(Type.getType((Class<?>) value));
} else if (target.getType().asErasure().isAssignableFrom(Class.class) && value instanceof TypeDescription) { } else if (value instanceof TypeDescription && target.getType().asErasure().isAssignableFrom(Class.class)) {
return new Target.ForConstantPoolValue(Type.getType(((TypeDescription) value).getDescriptor())); return new Target.ForConstantPoolValue(Type.getType(((TypeDescription) value).getDescriptor()));
} else if (!target.getType().isPrimitive() && !target.getType().isArray() && value instanceof Serializable && target.getType().asErasure().isInstance(value)) { } else if (value instanceof Field && target.getType().represents(Object.class)) {
return Target.ForField.ReadBoxed.of(instrumentedType, new FieldDescription.ForLoadedField((Field) value));
} else if (value instanceof FieldDescription && target.getType().represents(Object.class)) {
return Target.ForField.ReadBoxed.of(instrumentedType, ((FieldDescription) value).asDefined());
} else if (value instanceof Serializable && !target.getType().isPrimitive() && !target.getType().isArray() && target.getType().asErasure().isInstance(value)) {
return Target.ForSerializedObject.of(target.getType().asErasure(), (Serializable) value); return Target.ForSerializedObject.of(target.getType().asErasure(), (Serializable) value);
} else { } else {
throw new IllegalStateException("Cannot map " + value + " as constant value of " + target.getType()); throw new IllegalStateException("Cannot map " + value + " as constant value of " + target.getType());
Expand Down Expand Up @@ -9370,7 +9449,7 @@ protected WithCustomMapping(Map<Class<? extends Annotation>, DynamicValue<?>> dy
} }


/** /**
* Binds the supplied annotation to the supplied fixed value. * Binds the supplied annotation to a type constant of the supplied value.
* *
* @param type The type of the annotation being bound. * @param type The type of the annotation being bound.
* @param value The type reference to bind to this annotation. * @param value The type reference to bind to this annotation.
Expand All @@ -9382,6 +9461,35 @@ public <T extends Annotation> WithCustomMapping bind(Class<? extends T> type, Ty
return bind(type, new DynamicValue.ForFixedValue(value)); return bind(type, new DynamicValue.ForFixedValue(value));
} }


/**
* Binds the supplied annotation to the value of the supplied field. The field must be visible by the
* instrumented type and must be declared by a super type of the instrumented field.
*
* @param type The type of the annotation being bound.
* @param value The type reference to bind to this annotation.
* @param <T> The annotation type.
* @return A new builder for an advice that considers the supplied annotation type during binding.
* @see DynamicValue.ForFixedValue
*/
public <T extends Annotation> WithCustomMapping bind(Class<? extends T> type, Field value) {
return bind(type, new DynamicValue.ForFixedValue(value));
}


/**
* Binds the supplied annotation to the value of the supplied field. The field must be visible by the
* instrumented type and must be declared by a super type of the instrumented field.
*
* @param type The type of the annotation being bound.
* @param value The type reference to bind to this annotation.
* @param <T> The annotation type.
* @return A new builder for an advice that considers the supplied annotation type during binding.
* @see DynamicValue.ForFixedValue
*/
public <T extends Annotation> WithCustomMapping bind(Class<? extends T> type, FieldDescription value) {
return bind(type, new DynamicValue.ForFixedValue(value));
}

/** /**
* Binds the supplied annotation to the supplied fixed value. * Binds the supplied annotation to the supplied fixed value.
* *
Expand Down

0 comments on commit d6d06d7

Please sign in to comment.