Skip to content

Commit

Permalink
Improved dynamic binding for annotations in advice. Includes fields a…
Browse files Browse the repository at this point in the history
…nd method parameters. Improved validation.
  • Loading branch information
raphw committed Nov 2, 2016
1 parent 6aae9a3 commit 37934e6
Show file tree
Hide file tree
Showing 9 changed files with 663 additions and 118 deletions.
253 changes: 202 additions & 51 deletions byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java

Large diffs are not rendered by default.

Expand Up @@ -88,15 +88,6 @@ public interface TypeDescription extends TypeDefinition, ByteCodeElement, TypeVa
*/
boolean isInstance(Object value);

/**
* Checks if {@code value} is an instance of the type represented by this instance or a wrapper instance of the
* corresponding primitive value.
*
* @param value The object of interest.
* @return {@code true} if the object is an instance or wrapper of the type described by this instance.
*/
boolean isInstanceOrWrapper(Object value);

/**
* Checks if this type is assignable from the type described by this instance, for example for
* {@code class Foo} and {@code class Bar extends Foo}, this method would return {@code true} for
Expand Down Expand Up @@ -293,6 +284,18 @@ public interface TypeDescription extends TypeDefinition, ByteCodeElement, TypeVa
*/
int getSegmentCount();

/**
* Returns a description of this type that represents this type as a boxed type for primitive types, unless its {@code void}.
* @return A description of this type in its boxed form.
*/
TypeDescription asBoxed();

/**
* Returns a description of this type that represents this type as an unboxed type for boxing types, unless its {@link Void}.
* @return A description of this type in its unboxed form.
*/
TypeDescription asUnboxed();

/**
* <p>
* Represents a generic type of the Java programming language. A non-generic {@link TypeDescription} is considered to be
Expand Down Expand Up @@ -7195,19 +7198,6 @@ public boolean isInstance(Object value) {
return isAssignableFrom(value.getClass());
}

@Override
public boolean isInstanceOrWrapper(Object value) {
return isInstance(value)
|| (represents(boolean.class) && value instanceof Boolean)
|| (represents(byte.class) && value instanceof Byte)
|| (represents(short.class) && value instanceof Short)
|| (represents(char.class) && value instanceof Character)
|| (represents(int.class) && value instanceof Integer)
|| (represents(long.class) && value instanceof Long)
|| (represents(float.class) && value instanceof Float)
|| (represents(double.class) && value instanceof Double);
}

@Override
public boolean isAnnotationValue(Object value) {
if ((represents(Class.class) && value instanceof TypeDescription)
Expand Down Expand Up @@ -7450,6 +7440,52 @@ public int getSegmentCount() {
: declaringType.getSegmentCount() + 1;
}

@Override
public TypeDescription asBoxed() {
if (represents(boolean.class)) {
return new ForLoadedType(Boolean.class);
} else if (represents(byte.class)) {
return new ForLoadedType(Byte.class);
} else if (represents(short.class)) {
return new ForLoadedType(Short.class);
} else if (represents(char.class)) {
return new ForLoadedType(Character.class);
} else if (represents(int.class)) {
return new ForLoadedType(Integer.class);
} else if (represents(long.class)) {
return new ForLoadedType(Long.class);
} else if (represents(float.class)) {
return new ForLoadedType(Float.class);
} else if (represents(double.class)) {
return new ForLoadedType(Double.class);
} else {
return this;
}
}

@Override
public TypeDescription asUnboxed() {
if (represents(Boolean.class)) {
return new ForLoadedType(boolean.class);
} else if (represents(Byte.class)) {
return new ForLoadedType(byte.class);
} else if (represents(Short.class)) {
return new ForLoadedType(short.class);
} else if (represents(Character.class)) {
return new ForLoadedType(char.class);
} else if (represents(Integer.class)) {
return new ForLoadedType(int.class);
} else if (represents(Long.class)) {
return new ForLoadedType(long.class);
} else if (represents(Float.class)) {
return new ForLoadedType(float.class);
} else if (represents(Double.class)) {
return new ForLoadedType(double.class);
} else {
return this;
}
}

@Override
public Iterator<TypeDefinition> iterator() {
return new SuperClassIterator(this);
Expand Down
Expand Up @@ -3404,7 +3404,7 @@ protected OfInstance(MethodDescription.InDefinedShape bootstrapMethod,

@Override
public InvokeDynamic as(TypeDescription typeDescription) {
if (!typeDescription.isInstanceOrWrapper(value)) {
if (!typeDescription.asBoxed().isInstance(value)) {
throw new IllegalArgumentException(value + " is not of type " + typeDescription);
}
return new InvokeDynamic(bootstrapMethod,
Expand Down
Expand Up @@ -6,6 +6,7 @@

import java.io.Serializable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Member;

Expand Down Expand Up @@ -34,6 +35,11 @@ public enum JavaType {
*/
CALL_SITE("java.lang.invoke.CallSite", Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, Object.class),

/**
* The Java 7 {@code java.lang.reflect.Parameter} type.
*/
PARAMETER("java.lang.reflect.Parameter", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, Object.class, AnnotatedElement.class),

/**
* The {@code java.lang.reflect.Executable} type.
*/
Expand Down
Expand Up @@ -2,7 +2,6 @@

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -18,7 +17,7 @@
import static org.hamcrest.core.Is.is;

@RunWith(Parameterized.class)
public class AdviceBoxedFieldTest {
public class AdviceCustomAnnotationOnFieldTest {

private static final String FOO = "foo";

Expand All @@ -41,18 +40,18 @@ public static Collection<Object[]> data() {

private final Object expected;

public AdviceBoxedFieldTest(Class<?> target, Object expected) {
public AdviceCustomAnnotationOnFieldTest(Class<?> target, Object expected) {
this.target = target;
this.expected = expected;
}

@Test
public void testFieldValueAdvice() throws Exception {
public void testPrimitiveField() throws Exception {
Class<?> type = new ByteBuddy()
.redefine(target)
.visit(Advice.withCustomMapping()
.bind(FieldValue.class, target.getDeclaredField(FOO))
.to(FieldAdvice.class)
.bind(FieldValue.class, new FieldDescription.ForLoadedField(target.getDeclaredField(FOO)))
.to(target)
.on(named(FOO)))
.make()
.load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER)
Expand All @@ -61,23 +60,23 @@ public void testFieldValueAdvice() throws Exception {
}

@Test
public void testFieldDescriptionValueAdvice() throws Exception {
public void testBoxedField() throws Exception {
Class<?> type = new ByteBuddy()
.redefine(target)
.visit(Advice.withCustomMapping()
.bind(FieldValue.class, new FieldDescription.ForLoadedField(target.getDeclaredField(FOO)))
.to(FieldAdvice.class)
.to(BoxedFieldAdvice.class)
.on(named(FOO)))
.make()
.load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is(expected));
}

public static class FieldAdvice {
public static class BoxedFieldAdvice {

@Advice.OnMethodExit
static void foo(@FieldValue Object value, @Advice.Return(readOnly = false) Object returned) {
static void exit(@FieldValue Object value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}
Expand All @@ -94,6 +93,11 @@ public static class BooleanValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue boolean value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class ByteValue {
Expand All @@ -103,6 +107,11 @@ public static class ByteValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue byte value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class ShortValue {
Expand All @@ -112,6 +121,11 @@ public static class ShortValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue short value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class CharacterValue {
Expand All @@ -121,6 +135,11 @@ public static class CharacterValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue char value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class IntegerValue {
Expand All @@ -130,6 +149,11 @@ public static class IntegerValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue int value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class LongValue {
Expand All @@ -139,6 +163,11 @@ public static class LongValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue long value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class FloatValue {
Expand All @@ -148,6 +177,11 @@ public static class FloatValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue float value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class DoubleValue {
Expand All @@ -157,14 +191,24 @@ public static class DoubleValue {
public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue double value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}

public static class ReferenceValue {

Object foo = FOO;
String foo = FOO;

public Object foo() {
return null;
}

@Advice.OnMethodExit
static void exit(@FieldValue String value, @Advice.Return(readOnly = false) Object returned) {
returned = value;
}
}
}

0 comments on commit 37934e6

Please sign in to comment.