Skip to content

Commit

Permalink
Added further tests and comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rafael Winterhalter committed Apr 14, 2015
1 parent bfab770 commit 1a08bac
Show file tree
Hide file tree
Showing 17 changed files with 481 additions and 319 deletions.
304 changes: 212 additions & 92 deletions byte-buddy-dep/src/main/java/net/bytebuddy/ByteBuddy.java

Large diffs are not rendered by default.

69 changes: 54 additions & 15 deletions byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/DynamicType.java
Expand Up @@ -170,7 +170,8 @@ public interface DynamicType {
File toJar(File file, Manifest manifest) throws IOException; File toJar(File file, Manifest manifest) throws IOException;


/** /**
* A builder for defining a dynamic type. Implementations of such builders are usually immutable. * A builder for defining a dynamic type. Implementations of such builders are fully immutable and return
* modified instances.
* *
* @param <T> The most specific known loaded type that is implemented by the created dynamic type, usually the * @param <T> The most specific known loaded type that is implemented by the created dynamic type, usually the
* type itself, an interface or the direct super class. * type itself, an interface or the direct super class.
Expand Down Expand Up @@ -217,6 +218,12 @@ interface Builder<T> {
*/ */
Builder<T> name(NamingStrategy namingStrategy); Builder<T> name(NamingStrategy namingStrategy);


/**
* Defines a naming strategy for naming auxiliary types.
*
* @param namingStrategy The naming strategy to use.
* @return This builder where the auxiliary naming strategy was set to be used.
*/
Builder<T> name(AuxiliaryType.NamingStrategy namingStrategy); Builder<T> name(AuxiliaryType.NamingStrategy namingStrategy);


/** /**
Expand Down Expand Up @@ -672,8 +679,24 @@ interface MatchedMethodInterception<S> {
*/ */
MethodAnnotationTarget<S> withoutCode(); MethodAnnotationTarget<S> withoutCode();


/**
* Defines a default annotation value to set for any matched method.
*
* @param value The value that the annotation property should set as a default.
* @param type The type of the annotation property.
* @return A builder which defines the given default value for all matched methods.
*/
MethodAnnotationTarget<S> withDefaultValue(Object value, Class<?> type); MethodAnnotationTarget<S> withDefaultValue(Object value, Class<?> type);


/**
* Defines a default annotation value to set for any matched method. The value is to be represented in a wrapper format,
* {@code enum} values should be handed as {@link net.bytebuddy.instrumentation.attribute.annotation.AnnotationDescription.EnumerationValue}
* instances, annotations as {@link net.bytebuddy.instrumentation.attribute.annotation.AnnotationDescription} instances and
* {@link Class} values as {@link TypeDescription} instances. Other values are handed in their raw format or as their wrapper types.
*
* @param value A non-loaded value that the annotation property should set as a default.
* @return A builder which defines the given default value for all matched methods.
*/
MethodAnnotationTarget<S> withDefaultValue(Object value); MethodAnnotationTarget<S> withDefaultValue(Object value);
} }


Expand Down Expand Up @@ -2447,8 +2470,14 @@ protected class DefaultMatchedMethodInterception implements MatchedMethodInterce
*/ */
private final List<MethodToken> methodTokens; private final List<MethodToken> methodTokens;


private DefaultMatchedMethodInterception(LatentMethodMatcher methodMatcher, /**
List<MethodToken> methodTokens) { * Creates a new instance of a default matched method interception.
*
* @param methodMatcher The latent method matcher that identifies this interception.
* @param methodTokens A list of all method tokens that were previously defined.
*/
protected DefaultMatchedMethodInterception(LatentMethodMatcher methodMatcher,
List<MethodToken> methodTokens) {
this.methodMatcher = methodMatcher; this.methodMatcher = methodMatcher;
this.methodTokens = methodTokens; this.methodTokens = methodTokens;
} }
Expand All @@ -2471,18 +2500,14 @@ public MethodAnnotationTarget<S> withoutCode() {


@Override @Override
public MethodAnnotationTarget<S> withDefaultValue(Object value, Class<?> type) { public MethodAnnotationTarget<S> withDefaultValue(Object value, Class<?> type) {
TypeDescription typeDescription = new TypeDescription.ForLoadedType(nonNull(type)); return withDefaultValue(AnnotationDescription.ForLoadedAnnotation.wrap(nonNull(value), new TypeDescription.ForLoadedType(nonNull(type))));
if (!typeDescription.isAnnotationValue()) {
throw new IllegalArgumentException("Not an annotation value: " + type);
}
return withDefaultValue(AnnotationDescription.ForLoadedAnnotation.wrap(value, typeDescription));
} }


@Override @Override
public MethodAnnotationTarget<S> withDefaultValue(Object value) { public MethodAnnotationTarget<S> withDefaultValue(Object value) {
return new DefaultMethodAnnotationTarget(methodTokens, return new DefaultMethodAnnotationTarget(methodTokens,
methodMatcher, methodMatcher,
new MethodRegistry.Handler.ForAnnotationValue(value), MethodRegistry.Handler.ForAnnotationValue.of(value),
MethodAttributeAppender.NoOp.INSTANCE); MethodAttributeAppender.NoOp.INSTANCE);
} }


Expand Down Expand Up @@ -2628,24 +2653,38 @@ private Builder<?> getDynamicTypeBuilder() {
*/ */
protected class DefaultMethodAnnotationTarget extends AbstractDelegatingBuilder<S> implements MethodAnnotationTarget<S> { protected class DefaultMethodAnnotationTarget extends AbstractDelegatingBuilder<S> implements MethodAnnotationTarget<S> {


private final LatentMethodMatcher methodMatcher;

/** /**
* A list of all method tokens that were previously defined. * A list of all method tokens that were previously defined.
*/ */
private final List<MethodToken> methodTokens; private final List<MethodToken> methodTokens;


/**
* A matcher that allows to identify the methods to be intercepted.
*/
private final LatentMethodMatcher methodMatcher;

/**
* The handler to apply to any matched method.
*/
private final MethodRegistry.Handler handler; private final MethodRegistry.Handler handler;


/** /**
* The method attribute appender factory to be applied to the matched methods. * The method attribute appender factory to be applied to the matched methods.
*/ */
private final MethodAttributeAppender.Factory attributeAppenderFactory; private final MethodAttributeAppender.Factory attributeAppenderFactory;


private DefaultMethodAnnotationTarget(List<MethodToken> methodTokens, /**
LatentMethodMatcher methodMatcher, * Creates a new default method annotation target.
MethodRegistry.Handler handler, *
MethodAttributeAppender.Factory attributeAppenderFactory) { * @param methodTokens A list of all method tokens that were previously defined.
* @param methodMatcher A matcher that allows to identify the methods to be intercepted.
* @param handler The handler to apply to any matched method.
* @param attributeAppenderFactory The method attribute appender factory to be applied to the matched methods.
*/
protected DefaultMethodAnnotationTarget(List<MethodToken> methodTokens,
LatentMethodMatcher methodMatcher,
MethodRegistry.Handler handler,
MethodAttributeAppender.Factory attributeAppenderFactory) {
this.methodMatcher = methodMatcher; this.methodMatcher = methodMatcher;
this.methodTokens = methodTokens; this.methodTokens = methodTokens;
this.handler = handler; this.handler = handler;
Expand Down
24 changes: 20 additions & 4 deletions byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/TargetType.java
Expand Up @@ -17,16 +17,32 @@ public final class TargetType {
*/ */
public static final TypeDescription DESCRIPTION = new TypeDescription.ForLoadedType(TargetType.class); public static final TypeDescription DESCRIPTION = new TypeDescription.ForLoadedType(TargetType.class);


public static TypeDescription resolve(TypeDescription typeDescription, TypeDescription instrumentedType) { /**
* Resolves the given type description with the actual target type if the {@code typeDescription} resembles the
* {@link TargetType} placeholder.
*
* @param typeDescription The type description to resolve.
* @param actualTargetType The actual type for which {@link TargetType} was a placeholder.
* @return The resolved type description.
*/
public static TypeDescription resolve(TypeDescription typeDescription, TypeDescription actualTargetType) {
return typeDescription.represents(TargetType.class) return typeDescription.represents(TargetType.class)
? instrumentedType ? actualTargetType
: typeDescription; : typeDescription;
} }


public static TypeList resolve(List<? extends TypeDescription> typeList, TypeDescription instrumentedType) { /**
* Resolves any type description in the given listwith the actual target type if the {@code typeDescription} resembles the
* {@link TargetType} placeholder.
*
* @param typeList The list to resolve.
* @param actualTargetType The actual type for which {@link TargetType} was a placeholder.
* @return The resolved list of type descriptions.
*/
public static TypeList resolve(List<? extends TypeDescription> typeList, TypeDescription actualTargetType) {
List<TypeDescription> typeDescriptions = new ArrayList<TypeDescription>(typeList.size()); List<TypeDescription> typeDescriptions = new ArrayList<TypeDescription>(typeList.size());
for (TypeDescription typeDescription : typeList) { for (TypeDescription typeDescription : typeList) {
typeDescriptions.add(resolve(typeDescription, instrumentedType)); typeDescriptions.add(resolve(typeDescription, actualTargetType));
} }
return new TypeList.Explicit(typeDescriptions); return new TypeList.Explicit(typeDescriptions);
} }
Expand Down
Expand Up @@ -226,12 +226,26 @@ class ForAnnotationValue implements Handler, Compiled {
*/ */
private final Object annotationValue; private final Object annotationValue;


/**
* Represents the given value as an annotation default value handler after validating its suitability.
*
* @param annotationValue The annotation value to represent.
* @return A handler for setting the given value as a default value for instrumented methods.
*/
public static Handler of(Object annotationValue) {
TypeDescription typeDescription = new TypeDescription.ForLoadedType(annotationValue.getClass());
if (!typeDescription.isAnnotationValue() && !typeDescription.isPrimitiveWrapper()) {
throw new IllegalArgumentException("Not an annotation value type: " + typeDescription);
}
return new ForAnnotationValue(annotationValue);
}

/** /**
* Creates a handler for defining a default annotation value for a method. * Creates a handler for defining a default annotation value for a method.
* *
* @param annotationValue The annotation value to set as a default value. * @param annotationValue The annotation value to set as a default value.
*/ */
public ForAnnotationValue(Object annotationValue) { protected ForAnnotationValue(Object annotationValue) {
this.annotationValue = annotationValue; this.annotationValue = annotationValue;
} }


Expand Down
Expand Up @@ -524,12 +524,12 @@ abstract class Default<S> implements TypeWriter<S> {
/** /**
* A flag for ASM not to automatically compute any information such as operand stack sizes and stack map frames. * A flag for ASM not to automatically compute any information such as operand stack sizes and stack map frames.
*/ */
protected int ASM_MANUAL_FLAG = 0; protected static final int ASM_MANUAL_FLAG = 0;


/** /**
* The ASM API version to use. * The ASM API version to use.
*/ */
protected int ASM_API_VERSION = Opcodes.ASM5; protected static final int ASM_API_VERSION = Opcodes.ASM5;


/** /**
* The instrumented type that is to be written. * The instrumented type that is to be written.
Expand Down
Expand Up @@ -14,7 +14,6 @@
import net.bytebuddy.instrumentation.attribute.TypeAttributeAppender; import net.bytebuddy.instrumentation.attribute.TypeAttributeAppender;
import net.bytebuddy.instrumentation.method.MethodDescription; import net.bytebuddy.instrumentation.method.MethodDescription;
import net.bytebuddy.instrumentation.method.MethodLookupEngine; import net.bytebuddy.instrumentation.method.MethodLookupEngine;
import net.bytebuddy.instrumentation.type.InstrumentedType;
import net.bytebuddy.instrumentation.type.TypeDescription; import net.bytebuddy.instrumentation.type.TypeDescription;
import net.bytebuddy.instrumentation.type.auxiliary.AuxiliaryType; import net.bytebuddy.instrumentation.type.auxiliary.AuxiliaryType;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
Expand Down
Expand Up @@ -15,18 +15,14 @@
import net.bytebuddy.instrumentation.attribute.TypeAttributeAppender; import net.bytebuddy.instrumentation.attribute.TypeAttributeAppender;
import net.bytebuddy.instrumentation.method.MethodDescription; import net.bytebuddy.instrumentation.method.MethodDescription;
import net.bytebuddy.instrumentation.method.MethodLookupEngine; import net.bytebuddy.instrumentation.method.MethodLookupEngine;
import net.bytebuddy.instrumentation.type.InstrumentedType;
import net.bytebuddy.instrumentation.type.TypeDescription; import net.bytebuddy.instrumentation.type.TypeDescription;
import net.bytebuddy.instrumentation.type.auxiliary.AuxiliaryType; import net.bytebuddy.instrumentation.type.auxiliary.AuxiliaryType;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.LatentMethodMatcher;


import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;


import static net.bytebuddy.matcher.ElementMatchers.*;

/** /**
* A dynamic type builder that redefines a given type, i.e. it replaces any redefined method with another implementation. * A dynamic type builder that redefines a given type, i.e. it replaces any redefined method with another implementation.
* *
Expand Down
Expand Up @@ -42,6 +42,7 @@ public class SubclassDynamicTypeBuilder<T> extends DynamicType.Builder.AbstractB
* *
* @param classFileVersion The class file version for the created dynamic type. * @param classFileVersion The class file version for the created dynamic type.
* @param namingStrategy The naming strategy for naming the dynamic type. * @param namingStrategy The naming strategy for naming the dynamic type.
* @param auxiliaryTypeNamingStrategy The naming strategy to apply to auxiliary types.
* @param superType The super class that the dynamic type should extend. * @param superType The super class that the dynamic type should extend.
* @param interfaceTypes A list of interfaces that should be implemented by the created dynamic type. * @param interfaceTypes A list of interfaces that should be implemented by the created dynamic type.
* @param modifiers The modifiers to be represented by the dynamic type. * @param modifiers The modifiers to be represented by the dynamic type.
Expand Down Expand Up @@ -100,6 +101,7 @@ public SubclassDynamicTypeBuilder(ClassFileVersion classFileVersion,
* *
* @param classFileVersion The class file version for the created dynamic type. * @param classFileVersion The class file version for the created dynamic type.
* @param namingStrategy The naming strategy for naming the dynamic type. * @param namingStrategy The naming strategy for naming the dynamic type.
* @param auxiliaryTypeNamingStrategy The naming strategy to apply to auxiliary types.
* @param superType The super class that the dynamic type should extend. * @param superType The super class that the dynamic type should extend.
* @param interfaceTypes A list of interfaces that should be implemented by the created dynamic type. * @param interfaceTypes A list of interfaces that should be implemented by the created dynamic type.
* @param modifiers The modifiers to be represented by the dynamic type. * @param modifiers The modifiers to be represented by the dynamic type.
Expand Down
Expand Up @@ -335,6 +335,13 @@ public String toString() {
*/ */
interface Factory { interface Factory {


/**
* Creates a new instrumentation target.
*
* @param finding The analyzed instrumented type.
* @param instrumentedMethods A list of all methods that are to be instrumented.
* @return A suitable instrumentation target.
*/
Target make(MethodLookupEngine.Finding finding, List<? extends MethodDescription> instrumentedMethods); Target make(MethodLookupEngine.Finding finding, List<? extends MethodDescription> instrumentedMethods);
} }


Expand Down
Expand Up @@ -48,6 +48,8 @@
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
public @interface FieldProxy { public @interface FieldProxy {


// TODO: Update tutorial

/** /**
* A placeholder name to indicate that a field name should be inferred by the name of the intercepted * A placeholder name to indicate that a field name should be inferred by the name of the intercepted
* method by the Java bean naming conventions. * method by the Java bean naming conventions.
Expand Down
Expand Up @@ -21,22 +21,13 @@
* to the method it intercepts.</li> * to the method it intercepts.</li>
* <li>If the annotated parameter is of type {@link java.lang.Class}, the parameter is assigned a reference of the * <li>If the annotated parameter is of type {@link java.lang.Class}, the parameter is assigned a reference of the
* type of the instrumented type.</li> * type of the instrumented type.</li>
* <li>If the annotated parameter is of type {@link java.lang.String}, the parameter is assigned a string describing * <li>If the annotated parameter is of type {@link java.lang.String}, the parameter is assigned a string with
* a unique method signature of the method it intercepts. This string is a concatenation of: * the value that would be returned by the {@link Method#toString()} method.
* <ul><li>The method's name</li>
* <li>The <i>(</i> symbol</li>
* <li>A list of the method's parameters'
* <a href=http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3>descriptors</a></li>
* <li>The <i>)</i> symbol</li>
* <li>The <a href=http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3>descriptor</a> of the
* method's return type</li></ul>
* This unique signature allows the unambiguous identification of a particular class's methods while avoid the rather
* expensive creation of a {@link java.lang.reflect.Method} instance.
* </li> * </li>
* <li>If the annotated type is {@code java.lang.invoke.MethodHandle}, a handle of the intercepted method is injected. * <li>If the annotated type is {@code java.lang.invoke.MethodHandle}, a handle of the intercepted method is injected.
* A {@code java.lang.invoke.MethodHandle} is stored in a class's constant pool and does therefore not face the same * A {@code java.lang.invoke.MethodHandle} is stored in a class's constant pool and does therefore not face the same
* runtime performance limitations as a {@link java.lang.reflect.Method} reference. Method handles are only supported * runtime performance limitations as a (non-cached) {@link java.lang.reflect.Method} reference. Method handles are
* for byte code versions starting from Java 7.</li> * only supported for byte code versions starting from Java 7.</li>
* <li>If the annotated type is {@code java.lang.invoke.MethodType}, a description of the intercepted method's type * <li>If the annotated type is {@code java.lang.invoke.MethodType}, a description of the intercepted method's type
* is injected. Method type descriptions are only supported for byte code versions starting from Java 7.</li> * is injected. Method type descriptions are only supported for byte code versions starting from Java 7.</li>
* </ol> * </ol>
Expand All @@ -50,6 +41,15 @@
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
public @interface Origin { public @interface Origin {


// TODO: Update tutorial

/**
* Determines if the value that is assigned by this annotation is cached. For values that can be stored in the constant pool,
* this value is ignored as such values are cached implicitly. As a result, this value currently only affects caching of
* {@link Method} instances.
*
* @return {@code true} if the value for this parameter should be cached in a {@code static} field inside the instrumented class.
*/
boolean cache() default true; boolean cache() default true;


/** /**
Expand Down
Expand Up @@ -276,7 +276,7 @@ public interface TypeDescription extends ByteCodeElement {
boolean isPrimitiveWrapper(); boolean isPrimitiveWrapper();


/** /**
* Checks if instances of this type can be defined as values of an annotation. * Checks if instances of this type can be defined as unloaded value of an annotation.
* *
* @return {@code true} if instances of this type can be defined as values of an annotation. * @return {@code true} if instances of this type can be defined as values of an annotation.
*/ */
Expand Down Expand Up @@ -429,8 +429,8 @@ public boolean isPrimitiveWrapper() {
public boolean isAnnotationValue() { public boolean isAnnotationValue() {
return isPrimitive() return isPrimitive()
|| represents(String.class) || represents(String.class)
|| isEnum() || isAssignableTo(AnnotationDescription.EnumerationValue.class)
|| isAnnotation() || isAssignableTo(AnnotationDescription.class)
|| isAssignableTo(TypeDescription.class) || isAssignableTo(TypeDescription.class)
|| (isArray() && !getComponentType().isArray() && getComponentType().isAnnotationValue()); || (isArray() && !getComponentType().isArray() && getComponentType().isAnnotationValue());
} }
Expand Down
Expand Up @@ -259,14 +259,6 @@ public static <T> ElementMatcher.Junction<T> noneOf(Iterable<?> values) {
return matcher; return matcher;
} }


public static <T> ElementMatcher.Junction<T> matchesAny(Iterable<? extends ElementMatcher<? super T>> matchers) {
ElementMatcher.Junction<T> matcher = none();
for (ElementMatcher<? super T> aMatcher : matchers) {
matcher = matcher.or(aMatcher);
}
return matcher;
}

/** /**
* Creates a matcher that matches none of the given types as {@link net.bytebuddy.instrumentation.type.TypeDescription}s * Creates a matcher that matches none of the given types as {@link net.bytebuddy.instrumentation.type.TypeDescription}s
* by the {@link java.lang.Object#equals(Object)} method. None of the values must be {@code null}. * by the {@link java.lang.Object#equals(Object)} method. None of the values must be {@code null}.
Expand Down

0 comments on commit 1a08bac

Please sign in to comment.