/*
 * Copyright 2014 - 2020 Rafael Winterhalter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.bytebuddy.implementation.bind.annotation;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.ExceptionMethod;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.MethodAccessorFactory;
import net.bytebuddy.implementation.auxiliary.AuxiliaryType;
import net.bytebuddy.implementation.bind.MethodDelegationBinder;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.Duplication;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.TypeCreation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.Serializable;
import java.lang.annotation.*;
import java.util.Collections;

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

Using this annotation it is possible to access fields by getter and setter types. Before this annotation can be used, it needs to be installed with two types. The getter type must be defined in a single-method interface with a single method that returns an Object type and takes no arguments. The setter interface must similarly return void and take a single Object argument. After installing these interfaces with the Binder, this binder needs to be registered with a MethodDelegation before it can be used.
See Also:
/** * Using this annotation it is possible to access fields by getter and setter types. Before this annotation can be * used, it needs to be installed with two types. The getter type must be defined in a single-method interface * with a single method that returns an {@link java.lang.Object} type and takes no arguments. The setter interface * must similarly return {@code void} and take a single {@link java.lang.Object} argument. After installing these * interfaces with the {@link FieldProxy.Binder}, this * binder needs to be registered with a {@link net.bytebuddy.implementation.MethodDelegation} before it can be used. * * @see net.bytebuddy.implementation.MethodDelegation * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder */
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface FieldProxy {
Determines if the proxy should be serializable.
Returns:true if the proxy should be serializable.
/** * Determines if the proxy should be serializable. * * @return {@code true} if the proxy should be serializable. */
boolean serializableProxy() default false;
Determines the name of the field that is to be accessed. If this property is not set, a field name is inferred by the intercepted method after the Java beans naming conventions.
Returns:The name of the field to be accessed.
/** * Determines the name of the field that is to be accessed. If this property is not set, a field name is inferred * by the intercepted method after the Java beans naming conventions. * * @return The name of the field to be accessed. */
String value() default TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFieldBinding.BEAN_PROPERTY;
Determines which type defines the field that is to be accessed. If this property is not set, the most field that is defined highest in the type hierarchy is accessed.
Returns:The type that defines the accessed field.
/** * Determines which type defines the field that is to be accessed. If this property is not set, the most field * that is defined highest in the type hierarchy is accessed. * * @return The type that defines the accessed field. */
Class<?> declaringType() default void.class;
A binder for the FieldProxy annotation.
/** * A binder for the {@link FieldProxy} annotation. */
@HashCodeAndEqualsPlugin.Enhance class Binder extends TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFieldBinding<FieldProxy> {
A reference to the method that declares the field annotation's defining type property.
/** * A reference to the method that declares the field annotation's defining type property. */
private static final MethodDescription.InDefinedShape DECLARING_TYPE;
A reference to the method that declares the field annotation's field name property.
/** * A reference to the method that declares the field annotation's field name property. */
private static final MethodDescription.InDefinedShape FIELD_NAME;
A reference to the method that declares the field annotation's serializable proxy property.
/** * A reference to the method that declares the field annotation's serializable proxy property. */
private static final MethodDescription.InDefinedShape SERIALIZABLE_PROXY; /* * Fetches a reference to all annotation properties. */ static { MethodList<MethodDescription.InDefinedShape> methodList = TypeDescription.ForLoadedType.of(FieldProxy.class).getDeclaredMethods(); DECLARING_TYPE = methodList.filter(named("declaringType")).getOnly(); FIELD_NAME = methodList.filter(named("value")).getOnly(); SERIALIZABLE_PROXY = methodList.filter(named("serializableProxy")).getOnly(); }
Creates a binder by installing a single proxy type where annotating a parameter with FieldProxy allows getting and setting values for a given field.
Params:
  • type – A type which declares exactly one abstract getter and an abstract setter for the Object type. The type is allowed to be generic.
Returns:A binder for the FieldProxy annotation.
/** * Creates a binder by installing a single proxy type where annotating a parameter with {@link FieldProxy} allows * getting and setting values for a given field. * * @param type A type which declares exactly one abstract getter and an abstract setter for the {@link Object} * type. The type is allowed to be generic. * @return A binder for the {@link FieldProxy} annotation. */
public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(Class<?> type) { return install(TypeDescription.ForLoadedType.of(type)); }
Creates a binder by installing a single proxy type where annotating a parameter with FieldProxy allows getting and setting values for a given field.
Params:
  • typeDescription – A type which declares exactly one abstract getter and an abstract setter for the Object type. The type is allowed to be generic.
Returns:A binder for the FieldProxy annotation.
/** * Creates a binder by installing a single proxy type where annotating a parameter with {@link FieldProxy} allows * getting and setting values for a given field. * * @param typeDescription A type which declares exactly one abstract getter and an abstract setter for the {@link Object} * type. The type is allowed to be generic. * @return A binder for the {@link FieldProxy} annotation. */
public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(TypeDescription typeDescription) { if (!typeDescription.isInterface()) { throw new IllegalArgumentException(typeDescription + " is not an interface"); } else if (!typeDescription.getInterfaces().isEmpty()) { throw new IllegalArgumentException(typeDescription + " must not extend other interfaces"); } else if (!typeDescription.isPublic()) { throw new IllegalArgumentException(typeDescription + " is not public"); } MethodList<MethodDescription.InDefinedShape> methodCandidates = typeDescription.getDeclaredMethods().filter(isAbstract()); if (methodCandidates.size() != 2) { throw new IllegalArgumentException(typeDescription + " does not declare exactly two non-abstract methods"); } MethodList<MethodDescription.InDefinedShape> getterCandidates = methodCandidates.filter(isGetter(Object.class)); if (getterCandidates.size() != 1) { throw new IllegalArgumentException(typeDescription + " does not declare a getter with an Object type"); } MethodList<MethodDescription.InDefinedShape> setterCandidates = methodCandidates.filter(isSetter(Object.class)); if (setterCandidates.size() != 1) { throw new IllegalArgumentException(typeDescription + " does not declare a setter with an Object type"); } return new Binder(typeDescription, getterCandidates.getOnly(), setterCandidates.getOnly()); }
Creates a binder by installing two proxy types which are implemented by this binder if a field getter or a field setter is requested by using the FieldProxy annotation.
Params:
  • getterType – The type which should be used for getter proxies. The type must represent an interface which defines a single method which returns an Object return type and does not take any arguments. The use of generics is permitted.
  • setterType – The type which should be uses for setter proxies. The type must represent an interface which defines a single method which returns void and takes a single Object-typed argument. The use of generics is permitted.
Returns:A binder for the FieldProxy annotation.
/** * Creates a binder by installing two proxy types which are implemented by this binder if a field getter * or a field setter is requested by using the * {@link FieldProxy} annotation. * * @param getterType The type which should be used for getter proxies. The type must * represent an interface which defines a single method which returns an * {@link java.lang.Object} return type and does not take any arguments. The use of generics * is permitted. * @param setterType The type which should be uses for setter proxies. The type must * represent an interface which defines a single method which returns {@code void} * and takes a single {@link java.lang.Object}-typed argument. The use of generics * is permitted. * @return A binder for the {@link FieldProxy} annotation. */
public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(Class<?> getterType, Class<?> setterType) { return install(TypeDescription.ForLoadedType.of(getterType), TypeDescription.ForLoadedType.of(setterType)); }
Creates a binder by installing two proxy types which are implemented by this binder if a field getter or a field setter is requested by using the FieldProxy annotation.
Params:
  • getterType – The type which should be used for getter proxies. The type must represent an interface which defines a single method which returns an Object return type and does not take any arguments. The use of generics is permitted.
  • setterType – The type which should be uses for setter proxies. The type must represent an interface which defines a single method which returns void and takes a single Object-typed argument. The use of generics is permitted.
Returns:A binder for the FieldProxy annotation.
/** * Creates a binder by installing two proxy types which are implemented by this binder if a field getter * or a field setter is requested by using the * {@link FieldProxy} annotation. * * @param getterType The type which should be used for getter proxies. The type must * represent an interface which defines a single method which returns an * {@link java.lang.Object} return type and does not take any arguments. The use of generics * is permitted. * @param setterType The type which should be uses for setter proxies. The type must * represent an interface which defines a single method which returns {@code void} * and takes a single {@link java.lang.Object}-typed argument. The use of generics * is permitted. * @return A binder for the {@link FieldProxy} annotation. */
public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(TypeDescription getterType, TypeDescription setterType) { MethodDescription.InDefinedShape getterMethod = onlyMethod(getterType); if (!getterMethod.getReturnType().asErasure().represents(Object.class)) { throw new IllegalArgumentException(getterMethod + " must take a single Object-typed parameter"); } else if (getterMethod.getParameters().size() != 0) { throw new IllegalArgumentException(getterMethod + " must not declare parameters"); } MethodDescription.InDefinedShape setterMethod = onlyMethod(setterType); if (!setterMethod.getReturnType().asErasure().represents(void.class)) { throw new IllegalArgumentException(setterMethod + " must return void"); } else if (setterMethod.getParameters().size() != 1 || !setterMethod.getParameters().get(0).getType().asErasure().represents(Object.class)) { throw new IllegalArgumentException(setterMethod + " must declare a single Object-typed parameters"); } return new Binder(getterMethod, setterMethod); }
Extracts the only method from a given type description which is validated for the required properties for using the type as a proxy base type.
Params:
  • typeDescription – The type description to evaluate.
Returns:The only method which was found to be compatible to the proxy requirements.
/** * Extracts the only method from a given type description which is validated for the required properties for * using the type as a proxy base type. * * @param typeDescription The type description to evaluate. * @return The only method which was found to be compatible to the proxy requirements. */
private static MethodDescription.InDefinedShape onlyMethod(TypeDescription typeDescription) { if (!typeDescription.isInterface()) { throw new IllegalArgumentException(typeDescription + " is not an interface"); } else if (!typeDescription.getInterfaces().isEmpty()) { throw new IllegalArgumentException(typeDescription + " must not extend other interfaces"); } else if (!typeDescription.isPublic()) { throw new IllegalArgumentException(typeDescription + " is not public"); } MethodList<MethodDescription.InDefinedShape> methodCandidates = typeDescription.getDeclaredMethods().filter(isAbstract()); if (methodCandidates.size() != 1) { throw new IllegalArgumentException(typeDescription + " must declare exactly one abstract method"); } return methodCandidates.getOnly(); }
The field resolver factory to apply by this binder.
/** * The field resolver factory to apply by this binder. */
private final FieldResolver.Factory fieldResolverFactory;
Creates a new binder for a FieldProxy in simplex mode.
Params:
  • getterMethod – The getter method.
  • setterMethod – The setter method.
/** * Creates a new binder for a {@link FieldProxy} in simplex mode. * * @param getterMethod The getter method. * @param setterMethod The setter method. */
protected Binder(MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) { this(new FieldResolver.Factory.Simplex(getterMethod, setterMethod)); }
Creates a new binder for a FieldProxy in duplex mode.
Params:
  • proxyType – The proxy type.
  • getterMethod – The getter method.
  • setterMethod – The setter method.
/** * Creates a new binder for a {@link FieldProxy} in duplex mode. * * @param proxyType The proxy type. * @param getterMethod The getter method. * @param setterMethod The setter method. */
protected Binder(TypeDescription proxyType, MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) { this(new FieldResolver.Factory.Duplex(proxyType, getterMethod, setterMethod)); }
Creates a new binder for a FieldProxy.
Params:
  • fieldResolverFactory – The field resolver factory to apply by this binder.
/** * Creates a new binder for a {@link FieldProxy}. * * @param fieldResolverFactory The field resolver factory to apply by this binder. */
protected Binder(FieldResolver.Factory fieldResolverFactory) { this.fieldResolverFactory = fieldResolverFactory; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<FieldProxy> getHandledType() { return FieldProxy.class; } @Override protected String fieldName(AnnotationDescription.Loadable<FieldProxy> annotation) { return annotation.getValue(FIELD_NAME).resolve(String.class); } @Override protected TypeDescription declaringType(AnnotationDescription.Loadable<FieldProxy> annotation) { return annotation.getValue(DECLARING_TYPE).resolve(TypeDescription.class); } @Override protected MethodDelegationBinder.ParameterBinding<?> bind(FieldDescription fieldDescription, AnnotationDescription.Loadable<FieldProxy> annotation, MethodDescription source, ParameterDescription target, Implementation.Target implementationTarget, Assigner assigner) { FieldResolver fieldResolver = fieldResolverFactory.resolve(target.getType().asErasure(), fieldDescription); if (fieldResolver.isResolved()) { return new MethodDelegationBinder.ParameterBinding.Anonymous(new AccessorProxy(fieldDescription, implementationTarget.getInstrumentedType(), fieldResolver, assigner, annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class))); } else { return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE; } }
A resolver for creating an instrumentation for a field access.
/** * A resolver for creating an instrumentation for a field access. */
protected interface FieldResolver {
Returns true if the field access can be established.
Returns:true if the field access can be established.
/** * Returns {@code true} if the field access can be established. * * @return {@code true} if the field access can be established. */
boolean isResolved();
Returns the type of the field access proxy.
Returns:The type of the field access proxy.
/** * Returns the type of the field access proxy. * * @return The type of the field access proxy. */
TypeDescription getProxyType();
Applies this field resolver to a dynamic type.
Params:
  • builder – The dynamic type builder to use.
  • fieldDescription – The accessed field.
  • assigner – The assigner to use.
  • methodAccessorFactory – The method accessor factory to use.
Returns:The builder for creating the field accessor proxy type.
/** * Applies this field resolver to a dynamic type. * * @param builder The dynamic type builder to use. * @param fieldDescription The accessed field. * @param assigner The assigner to use. * @param methodAccessorFactory The method accessor factory to use. * @return The builder for creating the field accessor proxy type. */
DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, FieldDescription fieldDescription, Assigner assigner, MethodAccessorFactory methodAccessorFactory);
A factory for creating a field resolver.
/** * A factory for creating a field resolver. */
interface Factory {
Creates a field resolver.
Params:
  • parameterType – The type of the annotated parameter.
  • fieldDescription – The field being proxied.
Returns:An appropriate field resolver.
/** * Creates a field resolver. * * @param parameterType The type of the annotated parameter. * @param fieldDescription The field being proxied. * @return An appropriate field resolver. */
FieldResolver resolve(TypeDescription parameterType, FieldDescription fieldDescription);
A duplex factory for a type that both sets and gets a field value.
/** * A duplex factory for a type that both sets and gets a field value. */
@HashCodeAndEqualsPlugin.Enhance class Duplex implements Factory {
The type of the accessor proxy.
/** * The type of the accessor proxy. */
private final TypeDescription proxyType;
The getter method.
/** * The getter method. */
private final MethodDescription.InDefinedShape getterMethod;
The setter method.
/** * The setter method. */
private final MethodDescription.InDefinedShape setterMethod;
Creates a new duplex factory.
Params:
  • proxyType – The type of the accessor proxy.
  • getterMethod – The getter method.
  • setterMethod – The setter method.
/** * Creates a new duplex factory. * * @param proxyType The type of the accessor proxy. * @param getterMethod The getter method. * @param setterMethod The setter method. */
protected Duplex(TypeDescription proxyType, MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) { this.proxyType = proxyType; this.getterMethod = getterMethod; this.setterMethod = setterMethod; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldResolver resolve(TypeDescription parameterType, FieldDescription fieldDescription) { if (parameterType.equals(proxyType)) { return new ForGetterSetterPair(proxyType, getterMethod, setterMethod); } else { throw new IllegalStateException("Cannot use @FieldProxy on a non-installed type"); } } }
A simplex factory where field getters and setters both have their own type.
/** * A simplex factory where field getters and setters both have their own type. */
@HashCodeAndEqualsPlugin.Enhance class Simplex implements Factory {
The getter method.
/** * The getter method. */
private final MethodDescription.InDefinedShape getterMethod;
The setter method.
/** * The setter method. */
private final MethodDescription.InDefinedShape setterMethod;
Creates a simplex factory.
Params:
  • getterMethod – The getter method.
  • setterMethod – The setter method.
/** * Creates a simplex factory. * * @param getterMethod The getter method. * @param setterMethod The setter method. */
protected Simplex(MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) { this.getterMethod = getterMethod; this.setterMethod = setterMethod; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldResolver resolve(TypeDescription parameterType, FieldDescription fieldDescription) { if (parameterType.equals(getterMethod.getDeclaringType())) { return new ForGetter(getterMethod); } else if (parameterType.equals(setterMethod.getDeclaringType())) { return fieldDescription.isFinal() ? Unresolved.INSTANCE : new ForSetter(setterMethod); } else { throw new IllegalStateException("Cannot use @FieldProxy on a non-installed type"); } } } }
An unresolved field resolver.
/** * An unresolved field resolver. */
enum Unresolved implements FieldResolver {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isResolved() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public TypeDescription getProxyType() { throw new IllegalStateException("Cannot read type for unresolved field resolver"); }
{@inheritDoc}
/** * {@inheritDoc} */
public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, FieldDescription fieldDescription, Assigner assigner, MethodAccessorFactory methodAccessorFactory) { throw new IllegalStateException("Cannot apply unresolved field resolver"); } }
A field resolver for a getter accessor.
/** * A field resolver for a getter accessor. */
@HashCodeAndEqualsPlugin.Enhance class ForGetter implements FieldResolver {
The getter method.
/** * The getter method. */
private final MethodDescription.InDefinedShape getterMethod;
Creates a new getter field resolver.
Params:
  • getterMethod – The getter method.
/** * Creates a new getter field resolver. * * @param getterMethod The getter method. */
protected ForGetter(MethodDescription.InDefinedShape getterMethod) { this.getterMethod = getterMethod; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isResolved() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public TypeDescription getProxyType() { return getterMethod.getDeclaringType(); }
{@inheritDoc}
/** * {@inheritDoc} */
public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, FieldDescription fieldDescription, Assigner assigner, MethodAccessorFactory methodAccessorFactory) { return builder.method(definedMethod(is(getterMethod))).intercept(new FieldGetter(fieldDescription, assigner, methodAccessorFactory)); } }
A field resolver for a setter accessor.
/** * A field resolver for a setter accessor. */
@HashCodeAndEqualsPlugin.Enhance class ForSetter implements FieldResolver {
The setter method.
/** * The setter method. */
private final MethodDescription.InDefinedShape setterMethod;
Creates a new field resolver for a setter accessor.
Params:
  • setterMethod – The setter method.
/** * Creates a new field resolver for a setter accessor. * * @param setterMethod The setter method. */
protected ForSetter(MethodDescription.InDefinedShape setterMethod) { this.setterMethod = setterMethod; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isResolved() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public TypeDescription getProxyType() { return setterMethod.getDeclaringType(); }
{@inheritDoc}
/** * {@inheritDoc} */
public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, FieldDescription fieldDescription, Assigner assigner, MethodAccessorFactory methodAccessorFactory) { return builder.method(is(setterMethod)).intercept(new FieldSetter(fieldDescription, assigner, methodAccessorFactory)); } }
A field resolver for an accessor that both gets and sets a field value.
/** * A field resolver for an accessor that both gets and sets a field value. */
@HashCodeAndEqualsPlugin.Enhance class ForGetterSetterPair implements FieldResolver {
The type of the accessor proxy.
/** * The type of the accessor proxy. */
private final TypeDescription proxyType;
The getter method.
/** * The getter method. */
private final MethodDescription.InDefinedShape getterMethod;
The setter method.
/** * The setter method. */
private final MethodDescription.InDefinedShape setterMethod;
Creates a new field resolver for an accessor that both gets and sets a field value.
Params:
  • proxyType – The type of the accessor proxy.
  • getterMethod – The getter method.
  • setterMethod – The setter method.
/** * Creates a new field resolver for an accessor that both gets and sets a field value. * * @param proxyType The type of the accessor proxy. * @param getterMethod The getter method. * @param setterMethod The setter method. */
protected ForGetterSetterPair(TypeDescription proxyType, MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) { this.proxyType = proxyType; this.getterMethod = getterMethod; this.setterMethod = setterMethod; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isResolved() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public TypeDescription getProxyType() { return proxyType; }
{@inheritDoc}
/** * {@inheritDoc} */
public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, FieldDescription fieldDescription, Assigner assigner, MethodAccessorFactory methodAccessorFactory) { return builder .method(is(getterMethod)).intercept(new FieldGetter(fieldDescription, assigner, methodAccessorFactory)) .method(is(setterMethod)).intercept(fieldDescription.isFinal() ? ExceptionMethod.throwing(UnsupportedOperationException.class, "Cannot set final field " + fieldDescription) : new FieldSetter(fieldDescription, assigner, methodAccessorFactory)); } } }
Represents an implementation for implementing a proxy type constructor when a static field is accessed.
/** * Represents an implementation for implementing a proxy type constructor when a static field is accessed. */
protected enum StaticFieldConstructor implements Implementation {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
A reference of the Object type default constructor.
/** * A reference of the {@link Object} type default constructor. */
private final MethodDescription objectTypeDefaultConstructor;
Creates the constructor call singleton.
/** * Creates the constructor call singleton. */
StaticFieldConstructor() { objectTypeDefaultConstructor = TypeDescription.OBJECT.getDeclaredMethods().filter(isConstructor()).getOnly(); }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
public ByteCodeAppender appender(Target implementationTarget) { return new ByteCodeAppender.Simple(MethodVariableAccess.loadThis(), MethodInvocation.invoke(objectTypeDefaultConstructor), MethodReturn.VOID); } }
Represents an implementation for implementing a proxy type constructor when a non-static field is accessed.
/** * Represents an implementation for implementing a proxy type constructor when a non-static field is accessed. */
@HashCodeAndEqualsPlugin.Enhance protected static class InstanceFieldConstructor implements Implementation {
The instrumented type from which a field is to be accessed.
/** * The instrumented type from which a field is to be accessed. */
private final TypeDescription instrumentedType;
Creates a new implementation for implementing a field accessor proxy's constructor when accessing a non-static field.
Params:
  • instrumentedType – The instrumented type from which a field is to be accessed.
/** * Creates a new implementation for implementing a field accessor proxy's constructor when accessing * a non-static field. * * @param instrumentedType The instrumented type from which a field is to be accessed. */
protected InstanceFieldConstructor(TypeDescription instrumentedType) { this.instrumentedType = instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType.withField(new FieldDescription.Token(AccessorProxy.FIELD_NAME, Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE, this.instrumentedType.asGenericType())); }
{@inheritDoc}
/** * {@inheritDoc} */
public ByteCodeAppender appender(Target implementationTarget) { return new Appender(implementationTarget); }
An appender for implementing an InstanceFieldConstructor.
/** * An appender for implementing an * {@link FieldProxy.Binder.InstanceFieldConstructor}. */
@HashCodeAndEqualsPlugin.Enhance protected static class Appender implements ByteCodeAppender {
The field to be set within the constructor.
/** * The field to be set within the constructor. */
private final FieldDescription fieldDescription;
Creates a new appender.
Params:
  • implementationTarget – The implementation target of the current implementation.
/** * Creates a new appender. * * @param implementationTarget The implementation target of the current implementation. */
protected Appender(Target implementationTarget) { fieldDescription = implementationTarget.getInstrumentedType() .getDeclaredFields() .filter((named(AccessorProxy.FIELD_NAME))) .getOnly(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { StackManipulation.Size stackSize = new StackManipulation.Compound( MethodVariableAccess.loadThis(), MethodInvocation.invoke(StaticFieldConstructor.INSTANCE.objectTypeDefaultConstructor), MethodVariableAccess.allArgumentsOf(instrumentedMethod.asDefined()).prependThisReference(), FieldAccess.forField(fieldDescription).write(), MethodReturn.VOID ).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } } }
Implementation for a getter method.
/** * Implementation for a getter method. */
@HashCodeAndEqualsPlugin.Enhance protected static class FieldGetter implements Implementation {
The field that is being accessed.
/** * The field that is being accessed. */
private final FieldDescription fieldDescription;
The assigner to use.
/** * The assigner to use. */
private final Assigner assigner;
The accessed type's method accessor factory.
/** * The accessed type's method accessor factory. */
private final MethodAccessorFactory methodAccessorFactory;
Creates a new getter implementation.
Params:
  • fieldDescription – The field that is being accessed.
  • assigner – The assigner to use.
  • methodAccessorFactory – The accessed type's method accessor factory.
/** * Creates a new getter implementation. * * @param fieldDescription The field that is being accessed. * @param assigner The assigner to use. * @param methodAccessorFactory The accessed type's method accessor factory. */
protected FieldGetter(FieldDescription fieldDescription, Assigner assigner, MethodAccessorFactory methodAccessorFactory) { this.fieldDescription = fieldDescription; this.assigner = assigner; this.methodAccessorFactory = methodAccessorFactory; }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
public ByteCodeAppender appender(Target implementationTarget) { return new Appender(implementationTarget); }
A byte code appender for a getter method.
/** * A byte code appender for a getter method. */
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true) protected class Appender implements ByteCodeAppender {
The generated accessor type.
/** * The generated accessor type. */
private final TypeDescription typeDescription;
Creates a new appender for a setter method.
Params:
  • implementationTarget – The implementation target of the current instrumentation.
/** * Creates a new appender for a setter method. * * @param implementationTarget The implementation target of the current instrumentation. */
protected Appender(Target implementationTarget) { typeDescription = implementationTarget.getInstrumentedType(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { MethodDescription getterMethod = methodAccessorFactory.registerGetterFor(fieldDescription, MethodAccessorFactory.AccessType.DEFAULT); StackManipulation.Size stackSize = new StackManipulation.Compound( fieldDescription.isStatic() ? StackManipulation.Trivial.INSTANCE : new StackManipulation.Compound( MethodVariableAccess.loadThis(), FieldAccess.forField(typeDescription.getDeclaredFields().filter((named(AccessorProxy.FIELD_NAME))).getOnly()).read()), MethodInvocation.invoke(getterMethod), assigner.assign(getterMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC), MethodReturn.of(instrumentedMethod.getReturnType().asErasure()) ).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } } }
Implementation for a setter method.
/** * Implementation for a setter method. */
@HashCodeAndEqualsPlugin.Enhance protected static class FieldSetter implements Implementation {
The field that is being accessed.
/** * The field that is being accessed. */
private final FieldDescription fieldDescription;
The assigner to use.
/** * The assigner to use. */
private final Assigner assigner;
The accessed type's method accessor factory.
/** * The accessed type's method accessor factory. */
private final MethodAccessorFactory methodAccessorFactory;
Creates a new setter implementation.
Params:
  • fieldDescription – The field that is being accessed.
  • assigner – The assigner to use.
  • methodAccessorFactory – The accessed type's method accessor factory.
/** * Creates a new setter implementation. * * @param fieldDescription The field that is being accessed. * @param assigner The assigner to use. * @param methodAccessorFactory The accessed type's method accessor factory. */
protected FieldSetter(FieldDescription fieldDescription, Assigner assigner, MethodAccessorFactory methodAccessorFactory) { this.fieldDescription = fieldDescription; this.assigner = assigner; this.methodAccessorFactory = methodAccessorFactory; }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
public ByteCodeAppender appender(Target implementationTarget) { return new Appender(implementationTarget); }
A byte code appender for a setter method.
/** * A byte code appender for a setter method. */
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true) protected class Appender implements ByteCodeAppender {
The generated accessor type.
/** * The generated accessor type. */
private final TypeDescription typeDescription;
Creates a new appender for a setter method.
Params:
  • implementationTarget – The implementation target of the current instrumentation.
/** * Creates a new appender for a setter method. * * @param implementationTarget The implementation target of the current instrumentation. */
protected Appender(Target implementationTarget) { typeDescription = implementationTarget.getInstrumentedType(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { TypeDescription.Generic parameterType = instrumentedMethod.getParameters().get(0).getType(); MethodDescription setterMethod = methodAccessorFactory.registerSetterFor(fieldDescription, MethodAccessorFactory.AccessType.DEFAULT); StackManipulation.Size stackSize = new StackManipulation.Compound( fieldDescription.isStatic() ? StackManipulation.Trivial.INSTANCE : new StackManipulation.Compound( MethodVariableAccess.loadThis(), FieldAccess.forField(typeDescription.getDeclaredFields() .filter((named(AccessorProxy.FIELD_NAME))).getOnly()).read()), MethodVariableAccess.of(parameterType).loadFrom(1), assigner.assign(parameterType, setterMethod.getParameters().get(0).getType(), Assigner.Typing.DYNAMIC), MethodInvocation.invoke(setterMethod), MethodReturn.VOID ).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } } }
A proxy type for accessing a field either by a getter or a setter.
/** * A proxy type for accessing a field either by a getter or a setter. */
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true) protected class AccessorProxy implements AuxiliaryType, StackManipulation {
The name of the field that stores the accessed instance if any.
/** * The name of the field that stores the accessed instance if any. */
protected static final String FIELD_NAME = "instance";
The field that is being accessed.
/** * The field that is being accessed. */
private final FieldDescription fieldDescription;
The type which is accessed.
/** * The type which is accessed. */
private final TypeDescription instrumentedType;
The field resolver to use.
/** * The field resolver to use. */
private final FieldResolver fieldResolver;
The assigner to use.
/** * The assigner to use. */
private final Assigner assigner;
true if the generated proxy should be serializable.
/** * {@code true} if the generated proxy should be serializable. */
private final boolean serializableProxy;
Params:
  • fieldDescription – The field that is being accessed.
  • instrumentedType – The type which is accessed.
  • fieldResolver – The field resolver to use.
  • assigner – The assigner to use.
  • serializableProxy – true if the generated proxy should be serializable.
/** * @param fieldDescription The field that is being accessed. * @param instrumentedType The type which is accessed. * @param fieldResolver The field resolver to use. * @param assigner The assigner to use. * @param serializableProxy {@code true} if the generated proxy should be serializable. */
protected AccessorProxy(FieldDescription fieldDescription, TypeDescription instrumentedType, FieldResolver fieldResolver, Assigner assigner, boolean serializableProxy) { this.fieldDescription = fieldDescription; this.instrumentedType = instrumentedType; this.fieldResolver = fieldResolver; this.assigner = assigner; this.serializableProxy = serializableProxy; }
{@inheritDoc}
/** * {@inheritDoc} */
public DynamicType make(String auxiliaryTypeName, ClassFileVersion classFileVersion, MethodAccessorFactory methodAccessorFactory) { return fieldResolver.apply(new ByteBuddy(classFileVersion) .with(TypeValidation.DISABLED) .subclass(fieldResolver.getProxyType(), ConstructorStrategy.Default.NO_CONSTRUCTORS) .name(auxiliaryTypeName) .modifiers(DEFAULT_TYPE_MODIFIER) .implement(serializableProxy ? new Class<?>[]{Serializable.class} : new Class<?>[0]) .defineConstructor().withParameters(fieldDescription.isStatic() ? Collections.<TypeDescription>emptyList() : Collections.singletonList(instrumentedType)) .intercept(fieldDescription.isStatic() ? StaticFieldConstructor.INSTANCE : new InstanceFieldConstructor(instrumentedType)), fieldDescription, assigner, methodAccessorFactory).make(); }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { TypeDescription auxiliaryType = implementationContext.register(this); return new Compound( TypeCreation.of(auxiliaryType), Duplication.SINGLE, fieldDescription.isStatic() ? Trivial.INSTANCE : MethodVariableAccess.loadThis(), MethodInvocation.invoke(auxiliaryType.getDeclaredMethods().filter(isConstructor()).getOnly()) ).apply(methodVisitor, implementationContext); } } } }