java.net.bytebuddy.implementation.bind.annotation.FieldProxy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of securemock Show documentation
Show all versions of securemock Show documentation
Libraries for Elasticsearch
The newest version!
/*
* Copyright 2014 - 2018 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 {@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.
*
* @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.
*
* @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.
*
* @return The type that defines the accessed field.
*/
Class> declaringType() default void.class;
/**
* A binder for the {@link FieldProxy} annotation.
*/
@HashCodeAndEqualsPlugin.Enhance
class Binder extends TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFieldBinding {
/**
* 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.
*/
private static final MethodDescription.InDefinedShape FIELD_NAME;
/**
* 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 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 {@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 install(Class> type) {
return install(TypeDescription.ForLoadedType.of(type));
}
/**
* 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 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 methodCandidates = typeDescription.getDeclaredMethods().filter(isAbstract());
if (methodCandidates.size() != 2) {
throw new IllegalArgumentException(typeDescription + " does not declare exactly two non-abstract methods");
}
MethodList getterCandidates = methodCandidates.filter(isGetter(Object.class));
if (getterCandidates.size() != 1) {
throw new IllegalArgumentException(typeDescription + " does not declare a getter with an Object type");
}
MethodList 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
* {@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 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
* {@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 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.
*
* @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 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.
*/
private final FieldResolver.Factory fieldResolverFactory;
/**
* 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 {@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 {@link FieldProxy}.
*
* @param fieldResolverFactory The field resolver factory to apply by this binder.
*/
protected Binder(FieldResolver.Factory fieldResolverFactory) {
this.fieldResolverFactory = fieldResolverFactory;
}
/**
* {@inheritDoc}
*/
public Class getHandledType() {
return FieldProxy.class;
}
@Override
protected String fieldName(AnnotationDescription.Loadable annotation) {
return annotation.getValue(FIELD_NAME).resolve(String.class);
}
@Override
protected TypeDescription declaringType(AnnotationDescription.Loadable annotation) {
return annotation.getValue(DECLARING_TYPE).resolve(TypeDescription.class);
}
@Override
protected MethodDelegationBinder.ParameterBinding> bind(FieldDescription fieldDescription,
AnnotationDescription.Loadable 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.
*/
protected interface FieldResolver {
/**
* 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.
*
* @return The type of the field access proxy.
*/
TypeDescription getProxyType();
/**
* 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.
*/
interface Factory {
/**
* 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.
*/
@HashCodeAndEqualsPlugin.Enhance
class Duplex implements Factory {
/**
* The type of the accessor proxy.
*/
private final TypeDescription proxyType;
/**
* The getter method.
*/
private final MethodDescription.InDefinedShape getterMethod;
/**
* The setter method.
*/
private final MethodDescription.InDefinedShape setterMethod;
/**
* 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}
*/
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.
*/
@HashCodeAndEqualsPlugin.Enhance
class Simplex implements Factory {
/**
* The getter method.
*/
private final MethodDescription.InDefinedShape getterMethod;
/**
* The setter method.
*/
private final MethodDescription.InDefinedShape setterMethod;
/**
* 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}
*/
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.
*/
enum Unresolved implements FieldResolver {
/**
* The singleton instance.
*/
INSTANCE;
/**
* {@inheritDoc}
*/
public boolean isResolved() {
return false;
}
/**
* {@inheritDoc}
*/
public TypeDescription getProxyType() {
throw new IllegalStateException("Cannot read type for unresolved field resolver");
}
/**
* {@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.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForGetter implements FieldResolver {
/**
* The getter method.
*/
private final MethodDescription.InDefinedShape getterMethod;
/**
* Creates a new getter field resolver.
*
* @param getterMethod The getter method.
*/
protected ForGetter(MethodDescription.InDefinedShape getterMethod) {
this.getterMethod = getterMethod;
}
/**
* {@inheritDoc}
*/
public boolean isResolved() {
return true;
}
/**
* {@inheritDoc}
*/
public TypeDescription getProxyType() {
return getterMethod.getDeclaringType();
}
/**
* {@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.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForSetter implements FieldResolver {
/**
* The setter method.
*/
private final MethodDescription.InDefinedShape setterMethod;
/**
* Creates a new field resolver for a setter accessor.
*
* @param setterMethod The setter method.
*/
protected ForSetter(MethodDescription.InDefinedShape setterMethod) {
this.setterMethod = setterMethod;
}
/**
* {@inheritDoc}
*/
public boolean isResolved() {
return true;
}
/**
* {@inheritDoc}
*/
public TypeDescription getProxyType() {
return setterMethod.getDeclaringType();
}
/**
* {@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.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForGetterSetterPair implements FieldResolver {
/**
* The type of the accessor proxy.
*/
private final TypeDescription proxyType;
/**
* The getter method.
*/
private final MethodDescription.InDefinedShape getterMethod;
/**
* The setter method.
*/
private final MethodDescription.InDefinedShape setterMethod;
/**
* 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}
*/
public boolean isResolved() {
return true;
}
/**
* {@inheritDoc}
*/
public TypeDescription getProxyType() {
return proxyType;
}
/**
* {@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.
*/
protected enum StaticFieldConstructor implements Implementation {
/**
* The singleton instance.
*/
INSTANCE;
/**
* A reference of the {@link Object} type default constructor.
*/
private final MethodDescription objectTypeDefaultConstructor;
/**
* Creates the constructor call singleton.
*/
StaticFieldConstructor() {
objectTypeDefaultConstructor = TypeDescription.OBJECT.getDeclaredMethods().filter(isConstructor()).getOnly();
}
/**
* {@inheritDoc}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
/**
* {@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.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class InstanceFieldConstructor implements Implementation {
/**
* 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.
*
* @param instrumentedType The instrumented type from which a field is to be accessed.
*/
protected InstanceFieldConstructor(TypeDescription instrumentedType) {
this.instrumentedType = instrumentedType;
}
/**
* {@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}
*/
public ByteCodeAppender appender(Target implementationTarget) {
return new Appender(implementationTarget);
}
/**
* 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.
*/
private final FieldDescription fieldDescription;
/**
* 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}
*/
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.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class FieldGetter implements Implementation {
/**
* The field that is being accessed.
*/
private final FieldDescription fieldDescription;
/**
* The assigner to use.
*/
private final Assigner assigner;
/**
* The accessed type's method accessor factory.
*/
private final MethodAccessorFactory methodAccessorFactory;
/**
* 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}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
/**
* {@inheritDoc}
*/
public ByteCodeAppender appender(Target implementationTarget) {
return new Appender(implementationTarget);
}
/**
* A byte code appender for a getter method.
*/
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
protected class Appender implements ByteCodeAppender {
/**
* The generated accessor type.
*/
private final TypeDescription typeDescription;
/**
* 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}
*/
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.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class FieldSetter implements Implementation {
/**
* The field that is being accessed.
*/
private final FieldDescription fieldDescription;
/**
* The assigner to use.
*/
private final Assigner assigner;
/**
* The accessed type's method accessor factory.
*/
private final MethodAccessorFactory methodAccessorFactory;
/**
* 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}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
/**
* {@inheritDoc}
*/
public ByteCodeAppender appender(Target implementationTarget) {
return new Appender(implementationTarget);
}
/**
* A byte code appender for a setter method.
*/
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
protected class Appender implements ByteCodeAppender {
/**
* The generated accessor type.
*/
private final TypeDescription typeDescription;
/**
* 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}
*/
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.
*/
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
protected class AccessorProxy implements AuxiliaryType, StackManipulation {
/**
* 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.
*/
private final FieldDescription fieldDescription;
/**
* The type which is accessed.
*/
private final TypeDescription instrumentedType;
/**
* The field resolver to use.
*/
private final FieldResolver fieldResolver;
/**
* The assigner to use.
*/
private final Assigner assigner;
/**
* {@code true} if the generated proxy should be serializable.
*/
private final boolean serializableProxy;
/**
* @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}
*/
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.emptyList()
: Collections.singletonList(instrumentedType))
.intercept(fieldDescription.isStatic()
? StaticFieldConstructor.INSTANCE
: new InstanceFieldConstructor(instrumentedType)), fieldDescription, assigner, methodAccessorFactory).make();
}
/**
* {@inheritDoc}
*/
public boolean isValid() {
return true;
}
/**
* {@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);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy