/*
 * Copyright 2014 - 2019 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;

import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.scaffold.FieldLocator;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.constant.*;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import net.bytebuddy.utility.JavaConstant;
import net.bytebuddy.utility.JavaType;
import net.bytebuddy.utility.RandomString;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.lang.reflect.Field;
import java.lang.reflect.Type;

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

Defines a method to access a given field by following the Java bean conventions for getters and setters:

  • Getter: A method named getFoo() will be instrumented to read and return the value of a field foo or another field if one was specified explicitly. If a property is of type Boolean or boolean, the name isFoo() is also permitted.
  • Setter: A method named setFoo(value) will be instrumented to write the given argument value to a field foo or to another field if one was specified explicitly.

Field accessors always implement a getter if a non-void value is returned from a method and attempt to define a setter otherwise. If a field accessor is not explicitly defined as a setter via PropertyConfigurable, an instrumented method must define exactly one parameter. Using the latter API, an explicit parameter index can be defined and a return value can be specified explicitly when void is not returned.

/** * <p> * Defines a method to access a given field by following the Java bean conventions for getters and setters: * </p> * <ul> * <li>Getter: A method named {@code getFoo()} will be instrumented to read and return the value of a field {@code foo} * or another field if one was specified explicitly. If a property is of type {@link java.lang.Boolean} or * {@code boolean}, the name {@code isFoo()} is also permitted.</li> * <li>Setter: A method named {@code setFoo(value)} will be instrumented to write the given argument {@code value} * to a field {@code foo} or to another field if one was specified explicitly.</li> * </ul> * <p> * Field accessors always implement a getter if a non-{@code void} value is returned from a method and attempt to define a setter * otherwise. If a field accessor is not explicitly defined as a setter via {@link PropertyConfigurable}, an instrumented * method must define exactly one parameter. Using the latter API, an explicit parameter index can be defined and a return * value can be specified explicitly when {@code void} is not returned. * </p> */
@HashCodeAndEqualsPlugin.Enhance public abstract class FieldAccessor implements Implementation {
The field's location.
/** * The field's location. */
protected final FieldLocation fieldLocation;
The assigner to use.
/** * The assigner to use. */
protected final Assigner assigner;
Indicates if dynamic type castings should be attempted for incompatible assignments.
/** * Indicates if dynamic type castings should be attempted for incompatible assignments. */
protected final Assigner.Typing typing;
Creates a new field accessor.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
/** * Creates a new field accessor. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. */
protected FieldAccessor(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing) { this.fieldLocation = fieldLocation; this.assigner = assigner; this.typing = typing; }
Defines a field accessor where any access is targeted to a field named name.
Params:
  • name – The name of the field to be accessed.
Returns:A field accessor for a field of a given name.
/** * Defines a field accessor where any access is targeted to a field named {@code name}. * * @param name The name of the field to be accessed. * @return A field accessor for a field of a given name. */
public static OwnerTypeLocatable ofField(String name) { return of(new FieldNameExtractor.ForFixedValue(name)); }
Defines a field accessor where any access is targeted to a field that matches the methods name with the Java specification for bean properties, i.e. a method getFoo or setFoo(value) will either read or write a field named foo.
Returns:A field accessor that follows the Java naming conventions for bean properties.
/** * Defines a field accessor where any access is targeted to a field that matches the methods * name with the Java specification for bean properties, i.e. a method {@code getFoo} or {@code setFoo(value)} * will either read or write a field named {@code foo}. * * @return A field accessor that follows the Java naming conventions for bean properties. */
public static OwnerTypeLocatable ofBeanProperty() { return of(FieldNameExtractor.ForBeanProperty.INSTANCE); }
Defines a custom strategy for determining the field that is accessed by this field accessor.
Params:
  • fieldNameExtractor – The field name extractor to use.
Returns:A field accessor using the given field name extractor.
/** * Defines a custom strategy for determining the field that is accessed by this field accessor. * * @param fieldNameExtractor The field name extractor to use. * @return A field accessor using the given field name extractor. */
public static OwnerTypeLocatable of(FieldNameExtractor fieldNameExtractor) { return new ForImplicitProperty(new FieldLocation.Relative(fieldNameExtractor)); }
Defines a field accessor where the specified field is accessed. The field must be within the hierarchy of the instrumented type.
Params:
  • field – The field being accessed.
Returns:A field accessor for the given field.
/** * Defines a field accessor where the specified field is accessed. The field must be within the hierarchy of the instrumented type. * * @param field The field being accessed. * @return A field accessor for the given field. */
public static AssignerConfigurable of(Field field) { return of(new FieldDescription.ForLoadedField(field)); }
Defines a field accessor where the specified field is accessed. The field must be within the hierarchy of the instrumented type.
Params:
  • fieldDescription – The field being accessed.
Returns:A field accessor for the given field.
/** * Defines a field accessor where the specified field is accessed. The field must be within the hierarchy of the instrumented type. * * @param fieldDescription The field being accessed. * @return A field accessor for the given field. */
public static AssignerConfigurable of(FieldDescription fieldDescription) { return new ForImplicitProperty(new FieldLocation.Absolute(fieldDescription)); }
A field location represents an identified field description which depends on the instrumented type and method.
/** * A field location represents an identified field description which depends on the instrumented type and method. */
protected interface FieldLocation {
Specifies a field locator factory to use.
Params:
  • fieldLocatorFactory – The field locator factory to use.
Returns:An appropriate field location.
/** * Specifies a field locator factory to use. * * @param fieldLocatorFactory The field locator factory to use. * @return An appropriate field location. */
FieldLocation with(FieldLocator.Factory fieldLocatorFactory);
A prepared field location.
Params:
  • instrumentedType – The instrumented type.
Returns:A prepared field location.
/** * A prepared field location. * * @param instrumentedType The instrumented type. * @return A prepared field location. */
Prepared prepare(TypeDescription instrumentedType);
A prepared field location.
/** * A prepared field location. */
interface Prepared {
Resolves the field description to use.
Params:
  • instrumentedMethod – The instrumented method.
Returns:The resolved field description.
/** * Resolves the field description to use. * * @param instrumentedMethod The instrumented method. * @return The resolved field description. */
FieldDescription resolve(MethodDescription instrumentedMethod); }
An absolute field description representing a previously resolved field.
/** * An absolute field description representing a previously resolved field. */
@HashCodeAndEqualsPlugin.Enhance class Absolute implements FieldLocation, Prepared {
The field description.
/** * The field description. */
private final FieldDescription fieldDescription;
Creates an absolute field location.
Params:
  • fieldDescription – The field description.
/** * Creates an absolute field location. * * @param fieldDescription The field description. */
protected Absolute(FieldDescription fieldDescription) { this.fieldDescription = fieldDescription; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldLocation with(FieldLocator.Factory fieldLocatorFactory) { throw new IllegalStateException("Cannot specify a field locator factory for an absolute field location"); }
{@inheritDoc}
/** * {@inheritDoc} */
public Prepared prepare(TypeDescription instrumentedType) { if (!fieldDescription.isStatic() && !instrumentedType.isAssignableTo(fieldDescription.getDeclaringType().asErasure())) { throw new IllegalStateException(fieldDescription + " is not declared by " + instrumentedType); } else if (!fieldDescription.isAccessibleTo(instrumentedType)) { throw new IllegalStateException("Cannot access " + fieldDescription + " from " + instrumentedType); } return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldDescription resolve(MethodDescription instrumentedMethod) { return fieldDescription; } }
A relative field location where a field is located dynamically.
/** * A relative field location where a field is located dynamically. */
@HashCodeAndEqualsPlugin.Enhance class Relative implements FieldLocation {
The field name extractor to use.
/** * The field name extractor to use. */
private final FieldNameExtractor fieldNameExtractor;
The field locator factory to use.
/** * The field locator factory to use. */
private final FieldLocator.Factory fieldLocatorFactory;
Creates a new relative field location.
Params:
  • fieldNameExtractor – The field name extractor to use.
/** * Creates a new relative field location. * * @param fieldNameExtractor The field name extractor to use. */
protected Relative(FieldNameExtractor fieldNameExtractor) { this(fieldNameExtractor, FieldLocator.ForClassHierarchy.Factory.INSTANCE); }
Creates a new relative field location.
Params:
  • fieldNameExtractor – The field name extractor to use.
  • fieldLocatorFactory – The field locator factory to use.
/** * Creates a new relative field location. * * @param fieldNameExtractor The field name extractor to use. * @param fieldLocatorFactory The field locator factory to use. */
private Relative(FieldNameExtractor fieldNameExtractor, FieldLocator.Factory fieldLocatorFactory) { this.fieldNameExtractor = fieldNameExtractor; this.fieldLocatorFactory = fieldLocatorFactory; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldLocation with(FieldLocator.Factory fieldLocatorFactory) { return new Relative(fieldNameExtractor, fieldLocatorFactory); }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldLocation.Prepared prepare(TypeDescription instrumentedType) { return new Prepared(fieldNameExtractor, fieldLocatorFactory.make(instrumentedType)); }
A prepared version of a field location.
/** * A prepared version of a field location. */
@HashCodeAndEqualsPlugin.Enhance protected static class Prepared implements FieldLocation.Prepared {
The field name extractor to use.
/** * The field name extractor to use. */
private final FieldNameExtractor fieldNameExtractor;
The field locator factory to use.
/** * The field locator factory to use. */
private final FieldLocator fieldLocator;
Creates a new relative field location.
Params:
  • fieldNameExtractor – The field name extractor to use.
  • fieldLocator – The field locator to use.
/** * Creates a new relative field location. * * @param fieldNameExtractor The field name extractor to use. * @param fieldLocator The field locator to use. */
protected Prepared(FieldNameExtractor fieldNameExtractor, FieldLocator fieldLocator) { this.fieldNameExtractor = fieldNameExtractor; this.fieldLocator = fieldLocator; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldDescription resolve(MethodDescription instrumentedMethod) { FieldLocator.Resolution resolution = fieldLocator.locate(fieldNameExtractor.resolve(instrumentedMethod)); if (!resolution.isResolved()) { throw new IllegalStateException("Cannot resolve field for " + instrumentedMethod + " using " + fieldLocator); } return resolution.getField(); } } } }
A field name extractor is responsible for determining a field name to a method that is implemented to access this method.
/** * A field name extractor is responsible for determining a field name to a method that is implemented * to access this method. */
public interface FieldNameExtractor {
Extracts a field name to be accessed by a getter or setter method.
Params:
  • methodDescription – The method for which a field name is to be determined.
Returns:The name of the field to be accessed by this method.
/** * Extracts a field name to be accessed by a getter or setter method. * * @param methodDescription The method for which a field name is to be determined. * @return The name of the field to be accessed by this method. */
String resolve(MethodDescription methodDescription);
A FieldNameExtractor that determines a field name according to the rules of Java bean naming conventions.
/** * A {@link net.bytebuddy.implementation.FieldAccessor.FieldNameExtractor} that determines a field name * according to the rules of Java bean naming conventions. */
enum ForBeanProperty implements FieldNameExtractor {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public String resolve(MethodDescription methodDescription) { String name = methodDescription.getInternalName(); int crop; if (name.startsWith("get") || name.startsWith("set")) { crop = 3; } else if (name.startsWith("is")) { crop = 2; } else { throw new IllegalArgumentException(methodDescription + " does not follow Java bean naming conventions"); } name = name.substring(crop); if (name.length() == 0) { throw new IllegalArgumentException(methodDescription + " does not specify a bean name"); } return Character.toLowerCase(name.charAt(0)) + name.substring(1); } }
A field name extractor that returns a fixed value.
/** * A field name extractor that returns a fixed value. */
@HashCodeAndEqualsPlugin.Enhance class ForFixedValue implements FieldNameExtractor {
The name to return.
/** * The name to return. */
private final String name;
Creates a new field name extractor for a fixed value.
Params:
  • name – The name to return.
/** * Creates a new field name extractor for a fixed value. * * @param name The name to return. */
protected ForFixedValue(String name) { this.name = name; }
{@inheritDoc}
/** * {@inheritDoc} */
public String resolve(MethodDescription methodDescription) { return name; } } }
A field accessor that allows to define the access to be a field write of a given argument.
/** * A field accessor that allows to define the access to be a field write of a given argument. */
public interface PropertyConfigurable extends Implementation {

Defines a setter of the specified parameter for the field being described.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • index – The index of the parameter for which to set the field's value.
Returns:An instrumentation that sets the parameter's value to the described field.
/** * <p> * Defines a setter of the specified parameter for the field being described. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param index The index of the parameter for which to set the field's value. * @return An instrumentation that sets the parameter's value to the described field. */
Composable setsArgumentAt(int index);

Defines a setter of the described field's default value, i.e. null or a primitive type's representation of 0.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Returns:An instrumentation that sets the field's default value.
/** * <p> * Defines a setter of the described field's default value, i.e. {@code null} or a primitive type's * representation of {@code 0}. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @return An instrumentation that sets the field's default value. */
Composable setsDefaultValue();

Defines a setter of a given value for the described field. If the value is a constant value, it will be defined as a constant assignment, otherwise it is defined as a reference value that is stored in a static field of the instrumented type.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • value – The value to set.
Returns:An instrumentation that sets the field's value as specified.
/** * <p> * Defines a setter of a given value for the described field. If the value is a constant value, it will be * defined as a constant assignment, otherwise it is defined as a reference value that is stored in a static * field of the instrumented type. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param value The value to set. * @return An instrumentation that sets the field's value as specified. */
Composable setsValue(Object value);

Defines a setter of a given class constant value for the described field.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • typeDescription – The type to set to the described field.
Returns:An instrumentation that sets the field's value to the given class constant.
/** * <p> * Defines a setter of a given class constant value for the described field. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param typeDescription The type to set to the described field. * @return An instrumentation that sets the field's value to the given class constant. */
Composable setsValue(TypeDescription typeDescription);

Defines a setter of a given constant value for the described field.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • constant – The constant to set as a value.
Returns:An instrumentation that sets the field's value to the given constant.
/** * <p> * Defines a setter of a given constant value for the described field. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param constant The constant to set as a value. * @return An instrumentation that sets the field's value to the given constant. */
Composable setsValue(JavaConstant constant);

Defines a setter of a value that is represented by a stack manipulation.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • stackManipulation – A stack manipulation to load the field's value.
  • type – The field value's type.
Returns:An instrumentation that sets the field's value to the given value.
/** * <p> * Defines a setter of a value that is represented by a stack manipulation. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param stackManipulation A stack manipulation to load the field's value. * @param type The field value's type. * @return An instrumentation that sets the field's value to the given value. */
Composable setsValue(StackManipulation stackManipulation, Type type);

Defines a setter of a value that is represented by a stack manipulation.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • stackManipulation – A stack manipulation to load the field's value.
  • typeDescription – The field value's type.
Returns:An instrumentation that sets the field's value to the given value.
/** * <p> * Defines a setter of a value that is represented by a stack manipulation. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param stackManipulation A stack manipulation to load the field's value. * @param typeDescription The field value's type. * @return An instrumentation that sets the field's value to the given value. */
Composable setsValue(StackManipulation stackManipulation, TypeDescription.Generic typeDescription);

Defines a setter of a given value for the described field. The value is kept as a referenced that is stored in a static field of the instrumented type. The field name is chosen based on the value's hash code.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • value – The value to set.
Returns:An instrumentation that sets the field's value as specified.
/** * <p> * Defines a setter of a given value for the described field. The value is kept as a referenced that is stored * in a static field of the instrumented type. The field name is chosen based on the value's hash code. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param value The value to set. * @return An instrumentation that sets the field's value as specified. */
Composable setsReference(Object value);

Defines a setter of a given value for the described field. The value is kept as a referenced that is stored in a static field of the instrumented type.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • value – The value to set.
  • name – The name of the field.
Returns:An instrumentation that sets the field's value as specified.
/** * <p> * Defines a setter of a given value for the described field. The value is kept as a referenced that is stored * in a static field of the instrumented type. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param value The value to set. * @param name The name of the field. * @return An instrumentation that sets the field's value as specified. */
Composable setsReference(Object value, String name);

Defines a setter of a value that sets another field's value.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • field – The field that holds the value to be set.
Returns:An instrumentation that sets the field's value to the specified field's value.
/** * <p> * Defines a setter of a value that sets another field's value. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param field The field that holds the value to be set. * @return An instrumentation that sets the field's value to the specified field's value. */
Composable setsFieldValueOf(Field field);

Defines a setter of a value that sets another field's value.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • fieldDescription – The field that holds the value to be set.
Returns:An instrumentation that sets the field's value to the specified field's value.
/** * <p> * Defines a setter of a value that sets another field's value. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param fieldDescription The field that holds the value to be set. * @return An instrumentation that sets the field's value to the specified field's value. */
Composable setsFieldValueOf(FieldDescription fieldDescription);

Defines a setter of a value that sets another field's value.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • fieldName – The name of the field that is specified by the instrumented type.
Returns:An instrumentation that sets the field's value to the specified field's value.
/** * <p> * Defines a setter of a value that sets another field's value. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param fieldName The name of the field that is specified by the instrumented type. * @return An instrumentation that sets the field's value to the specified field's value. */
Composable setsFieldValueOf(String fieldName);

Defines a setter of a value that sets another field's value.

Note: If the instrumented method does not return void, a chained instrumentation must be supplied.

Params:
  • fieldNameExtractor – A field name extractor for the field that is specified by the instrumented type.
Returns:An instrumentation that sets the field's value to the specified field's value.
/** * <p> * Defines a setter of a value that sets another field's value. * </p> * <p> * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied. * </p> * * @param fieldNameExtractor A field name extractor for the field that is specified by the instrumented type. * @return An instrumentation that sets the field's value to the specified field's value. */
Composable setsFieldValueOf(FieldNameExtractor fieldNameExtractor); }
A field accessor that can be configured to use a given assigner and runtime type use configuration.
/** * A field accessor that can be configured to use a given assigner and runtime type use configuration. */
public interface AssignerConfigurable extends PropertyConfigurable {
Returns a field accessor that is identical to this field accessor but uses the given assigner and runtime type use configuration.
Params:
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
Returns:This field accessor with the given assigner and runtime type use configuration.
/** * Returns a field accessor that is identical to this field accessor but uses the given assigner * and runtime type use configuration. * * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. * @return This field accessor with the given assigner and runtime type use configuration. */
PropertyConfigurable withAssigner(Assigner assigner, Assigner.Typing typing); }
A field accessor that can be configured to locate a field in a specific manner.
/** * A field accessor that can be configured to locate a field in a specific manner. */
public interface OwnerTypeLocatable extends AssignerConfigurable {
Determines that a field should only be considered when it was defined in a given type.
Params:
  • type – The type to be considered.
Returns:This field accessor which will only considered fields that are defined in the given type.
/** * Determines that a field should only be considered when it was defined in a given type. * * @param type The type to be considered. * @return This field accessor which will only considered fields that are defined in the given type. */
AssignerConfigurable in(Class<?> type);
Determines that a field should only be considered when it was defined in a given type.
Params:
  • typeDescription – A description of the type to be considered.
Returns:This field accessor which will only considered fields that are defined in the given type.
/** * Determines that a field should only be considered when it was defined in a given type. * * @param typeDescription A description of the type to be considered. * @return This field accessor which will only considered fields that are defined in the given type. */
AssignerConfigurable in(TypeDescription typeDescription);
Determines that a field should only be considered when it was identified by a field locator that is produced by the given factory.
Params:
  • fieldLocatorFactory – A factory that will produce a field locator that will be used to find locate a field to be accessed.
Returns:This field accessor which will only considered fields that are defined in the given type.
/** * Determines that a field should only be considered when it was identified by a field locator that is * produced by the given factory. * * @param fieldLocatorFactory A factory that will produce a field locator that will be used to find locate * a field to be accessed. * @return This field accessor which will only considered fields that are defined in the given type. */
AssignerConfigurable in(FieldLocator.Factory fieldLocatorFactory); }
A field accessor for an implicit property where a getter or setter property is inferred from the signature.
/** * A field accessor for an implicit property where a getter or setter property is inferred from the signature. */
protected static class ForImplicitProperty extends FieldAccessor implements OwnerTypeLocatable {
Creates a field accessor for an implicit property.
Params:
  • fieldLocation – The field's location.
/** * Creates a field accessor for an implicit property. * * @param fieldLocation The field's location. */
protected ForImplicitProperty(FieldLocation fieldLocation) { this(fieldLocation, Assigner.DEFAULT, Assigner.Typing.STATIC); }
Creates a field accessor for an implicit property.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – The typing to use.
/** * Creates a field accessor for an implicit property. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing The typing to use. */
private ForImplicitProperty(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing) { super(fieldLocation, assigner, typing); }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
public ByteCodeAppender appender(Target implementationTarget) { return new Appender(fieldLocation.prepare(implementationTarget.getInstrumentedType())); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsArgumentAt(int index) { if (index < 0) { throw new IllegalArgumentException("A parameter index cannot be negative: " + index); } return new ForSetter.OfParameterValue(fieldLocation, assigner, typing, ForSetter.TerminationHandler.RETURNING, index); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsDefaultValue() { return new ForSetter.OfDefaultValue(fieldLocation, assigner, typing, ForSetter.TerminationHandler.RETURNING); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsValue(Object value) { Class<?> type = value.getClass(); if (type == String.class) { return setsValue(new TextConstant((String) value), String.class); } else if (type == Class.class) { return setsValue(ClassConstant.of(TypeDescription.ForLoadedType.of((Class<?>) value)), Class.class); } else if (type == Boolean.class) { return setsValue(IntegerConstant.forValue((Boolean) value), boolean.class); } else if (type == Byte.class) { return setsValue(IntegerConstant.forValue((Byte) value), byte.class); } else if (type == Short.class) { return setsValue(IntegerConstant.forValue((Short) value), short.class); } else if (type == Character.class) { return setsValue(IntegerConstant.forValue((Character) value), char.class); } else if (type == Integer.class) { return setsValue(IntegerConstant.forValue((Integer) value), int.class); } else if (type == Long.class) { return setsValue(LongConstant.forValue((Long) value), long.class); } else if (type == Float.class) { return setsValue(FloatConstant.forValue((Float) value), float.class); } else if (type == Double.class) { return setsValue(DoubleConstant.forValue((Double) value), double.class); } else if (JavaType.METHOD_HANDLE.getTypeStub().isAssignableFrom(type)) { return setsValue(new JavaConstantValue(JavaConstant.MethodHandle.ofLoaded(value)), type); } else if (JavaType.METHOD_TYPE.getTypeStub().represents(type)) { return setsValue(new JavaConstantValue(JavaConstant.MethodType.ofLoaded(value)), type); } else { return setsReference(value); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsValue(TypeDescription typeDescription) { return setsValue(ClassConstant.of(typeDescription), Class.class); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsValue(JavaConstant constant) { return setsValue(new JavaConstantValue(constant), constant.getType().asGenericType()); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsValue(StackManipulation stackManipulation, Type type) { return setsValue(stackManipulation, TypeDescription.Generic.Sort.describe(type)); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsValue(StackManipulation stackManipulation, TypeDescription.Generic typeDescription) { return new ForSetter.OfConstantValue(fieldLocation, assigner, typing, ForSetter.TerminationHandler.RETURNING, typeDescription, stackManipulation); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsReference(Object value) { return setsReference(value, ForSetter.OfReferenceValue.PREFIX + "$" + RandomString.hashOf(value.hashCode())); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsReference(Object value, String name) { return new ForSetter.OfReferenceValue(fieldLocation, assigner, typing, ForSetter.TerminationHandler.RETURNING, value, name); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsFieldValueOf(Field field) { return setsFieldValueOf(new FieldDescription.ForLoadedField(field)); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsFieldValueOf(FieldDescription fieldDescription) { return new ForSetter.OfFieldValue(fieldLocation, assigner, typing, ForSetter.TerminationHandler.RETURNING, new FieldLocation.Absolute(fieldDescription)); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsFieldValueOf(String fieldName) { return setsFieldValueOf(new FieldNameExtractor.ForFixedValue(fieldName)); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable setsFieldValueOf(FieldNameExtractor fieldNameExtractor) { return new ForSetter.OfFieldValue(fieldLocation, assigner, typing, ForSetter.TerminationHandler.RETURNING, new FieldLocation.Relative(fieldNameExtractor)); }
{@inheritDoc}
/** * {@inheritDoc} */
public PropertyConfigurable withAssigner(Assigner assigner, Assigner.Typing typing) { return new ForImplicitProperty(fieldLocation, assigner, typing); }
{@inheritDoc}
/** * {@inheritDoc} */
public AssignerConfigurable in(Class<?> type) { return in(TypeDescription.ForLoadedType.of(type)); }
{@inheritDoc}
/** * {@inheritDoc} */
public AssignerConfigurable in(TypeDescription typeDescription) { return in(new FieldLocator.ForExactType.Factory(typeDescription)); }
{@inheritDoc}
/** * {@inheritDoc} */
public AssignerConfigurable in(FieldLocator.Factory fieldLocatorFactory) { return new ForImplicitProperty(fieldLocation.with(fieldLocatorFactory), assigner, typing); }
An byte code appender for an field accessor implementation.
/** * An byte code appender for an field accessor implementation. */
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true) protected class Appender implements ByteCodeAppender {
The field's location.
/** * The field's location. */
private final FieldLocation.Prepared fieldLocation;
Creates a new byte code appender for a field accessor implementation.
Params:
  • fieldLocation – The field's location.
/** * Creates a new byte code appender for a field accessor implementation. * * @param fieldLocation The field's location. */
protected Appender(FieldLocation.Prepared fieldLocation) { this.fieldLocation = fieldLocation; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) { if (!instrumentedMethod.isMethod()) { throw new IllegalArgumentException(instrumentedMethod + " does not describe a field getter or setter"); } FieldDescription fieldDescription = fieldLocation.resolve(instrumentedMethod); if (!fieldDescription.isStatic() && instrumentedMethod.isStatic()) { throw new IllegalStateException("Cannot set instance field " + fieldDescription + " from " + instrumentedMethod); } StackManipulation implementation, initialization = fieldDescription.isStatic() ? StackManipulation.Trivial.INSTANCE : MethodVariableAccess.loadThis(); if (!instrumentedMethod.getReturnType().represents(void.class)) { implementation = new StackManipulation.Compound( initialization, FieldAccess.forField(fieldDescription).read(), assigner.assign(fieldDescription.getType(), instrumentedMethod.getReturnType(), typing), MethodReturn.of(instrumentedMethod.getReturnType()) ); } else if (instrumentedMethod.getReturnType().represents(void.class) && instrumentedMethod.getParameters().size() == 1) { if (fieldDescription.isFinal() && instrumentedMethod.isMethod()) { throw new IllegalStateException("Cannot set final field " + fieldDescription + " from " + instrumentedMethod); } implementation = new StackManipulation.Compound( initialization, MethodVariableAccess.load(instrumentedMethod.getParameters().get(0)), assigner.assign(instrumentedMethod.getParameters().get(0).getType(), fieldDescription.getType(), typing), FieldAccess.forField(fieldDescription).write(), MethodReturn.VOID ); } else { throw new IllegalArgumentException("Method " + instrumentedMethod + " is no bean accessor"); } if (!implementation.isValid()) { throw new IllegalStateException("Cannot set or get value of " + instrumentedMethod + " using " + fieldDescription); } return new Size(implementation.apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); } } }
A field accessor for a field setter.
Type parameters:
  • <T> – The type of the value that is initialized per instrumented type.
/** * A field accessor for a field setter. * * @param <T> The type of the value that is initialized per instrumented type. */
@HashCodeAndEqualsPlugin.Enhance protected abstract static class ForSetter<T> extends FieldAccessor implements Implementation.Composable {
The termination handler to apply.
/** * The termination handler to apply. */
private final TerminationHandler terminationHandler;
Creates a new field accessor for a setter instrumentation.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
  • terminationHandler – The termination handler to apply.
/** * Creates a new field accessor for a setter instrumentation. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. * @param terminationHandler The termination handler to apply. */
protected ForSetter(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler) { super(fieldLocation, assigner, typing); this.terminationHandler = terminationHandler; }
{@inheritDoc}
/** * {@inheritDoc} */
public ByteCodeAppender appender(Target implementationTarget) { return new Appender(implementationTarget.getInstrumentedType(), initialize(implementationTarget.getInstrumentedType()), fieldLocation.prepare(implementationTarget.getInstrumentedType())); }
Initializes a value to be used during method instrumentation.
Params:
  • instrumentedType – The instrumented type.
Returns:The initialized value.
/** * Initializes a value to be used during method instrumentation. * * @param instrumentedType The instrumented type. * @return The initialized value. */
protected abstract T initialize(TypeDescription instrumentedType);
Resolves the stack manipulation to load the value being set.
Params:
  • initialized – The method that was initialized for the instrumented type.
  • fieldDescription – The field to set the value for.
  • instrumentedType – The instrumented type.
  • instrumentedMethod – The instrumented method.
Returns:The stack manipulation to apply.
/** * Resolves the stack manipulation to load the value being set. * * @param initialized The method that was initialized for the instrumented type. * @param fieldDescription The field to set the value for. * @param instrumentedType The instrumented type. * @param instrumentedMethod The instrumented method. * @return The stack manipulation to apply. */
protected abstract StackManipulation resolve(T initialized, FieldDescription fieldDescription, TypeDescription instrumentedType, MethodDescription instrumentedMethod);
A termination handler is responsible for handling a field accessor's return.
/** * A termination handler is responsible for handling a field accessor's return. */
protected enum TerminationHandler {
Returns void or throws an exception if this is not the return type of the instrumented method.
/** * Returns {@code void} or throws an exception if this is not the return type of the instrumented method. */
RETURNING { @Override protected StackManipulation resolve(MethodDescription instrumentedMethod) { if (!instrumentedMethod.getReturnType().represents(void.class)) { throw new IllegalStateException("Cannot implement setter with return value for " + instrumentedMethod); } return MethodReturn.VOID; } },
Does not return from the method at all.
/** * Does not return from the method at all. */
NON_OPERATIONAL { @Override protected StackManipulation resolve(MethodDescription instrumentedMethod) { return StackManipulation.Trivial.INSTANCE; } };
Resolves the return instruction.
Params:
  • instrumentedMethod – The instrumented method.
Returns:An appropriate stack manipulation.
/** * Resolves the return instruction. * * @param instrumentedMethod The instrumented method. * @return An appropriate stack manipulation. */
protected abstract StackManipulation resolve(MethodDescription instrumentedMethod); }
A setter instrumentation for a parameter value.
/** * A setter instrumentation for a parameter value. */
@HashCodeAndEqualsPlugin.Enhance protected static class OfParameterValue extends ForSetter<Void> {
The parameter's index.
/** * The parameter's index. */
private final int index;
Creates a new setter instrumentation for a parameter value.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
  • terminationHandler – The termination handler to apply.
  • index – The parameter's index.
/** * Creates a new setter instrumentation for a parameter value. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. * @param terminationHandler The termination handler to apply. * @param index The parameter's index. */
protected OfParameterValue(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler, int index) { super(fieldLocation, assigner, typing, terminationHandler); this.index = index; }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
protected Void initialize(TypeDescription instrumentedType) { return null; }
{@inheritDoc}
/** * {@inheritDoc} */
protected StackManipulation resolve(Void unused, FieldDescription fieldDescription, TypeDescription instrumentedType, MethodDescription instrumentedMethod) { if (instrumentedMethod.getParameters().size() <= index) { throw new IllegalStateException(instrumentedMethod + " does not define a parameter with index " + index); } else { return new StackManipulation.Compound( MethodVariableAccess.load(instrumentedMethod.getParameters().get(index)), assigner.assign(instrumentedMethod.getParameters().get(index).getType(), fieldDescription.getType(), typing) ); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Implementation andThen(Implementation implementation) { return new Compound(new OfParameterValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, index), implementation); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable andThen(Composable implementation) { return new Compound.Composable(new OfParameterValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, index), implementation); } }
A setter instrumentation that sets a null or a primitive type's default value.
/** * A setter instrumentation that sets a {@code null} or a primitive type's default value. */
protected static class OfDefaultValue extends ForSetter<Void> {
Creates an intrumentation that sets a field's default value.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
  • terminationHandler – The termination handler to apply.
/** * Creates an intrumentation that sets a field's default value. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. * @param terminationHandler The termination handler to apply. */
protected OfDefaultValue(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler) { super(fieldLocation, assigner, typing, terminationHandler); }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
protected Void initialize(TypeDescription instrumentedType) { return null; }
{@inheritDoc}
/** * {@inheritDoc} */
protected StackManipulation resolve(Void initialized, FieldDescription fieldDescription, TypeDescription instrumentedType, MethodDescription instrumentedMethod) { return DefaultValue.of(fieldDescription.getType()); }
{@inheritDoc}
/** * {@inheritDoc} */
public Implementation andThen(Implementation implementation) { return new Compound(new OfDefaultValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL), implementation); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable andThen(Composable implementation) { return new Compound.Composable(new OfDefaultValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL), implementation); } }
An instrumentation that sets a constant value to a field.
/** * An instrumentation that sets a constant value to a field. */
@HashCodeAndEqualsPlugin.Enhance protected static class OfConstantValue extends ForSetter<Void> {
The value's type.
/** * The value's type. */
private final TypeDescription.Generic typeDescription;
A stack manipulation to load the constant value.
/** * A stack manipulation to load the constant value. */
private final StackManipulation stackManipulation;
Creates a setter instrumentation for setting a constant value.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
  • terminationHandler – The termination handler to apply.
  • typeDescription – The value's type.
  • stackManipulation – A stack manipulation to load the constant value.
/** * Creates a setter instrumentation for setting a constant value. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. * @param terminationHandler The termination handler to apply. * @param typeDescription The value's type. * @param stackManipulation A stack manipulation to load the constant value. */
protected OfConstantValue(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler, TypeDescription.Generic typeDescription, StackManipulation stackManipulation) { super(fieldLocation, assigner, typing, terminationHandler); this.typeDescription = typeDescription; this.stackManipulation = stackManipulation; }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
protected Void initialize(TypeDescription instrumentedType) { return null; }
{@inheritDoc}
/** * {@inheritDoc} */
protected StackManipulation resolve(Void unused, FieldDescription fieldDescription, TypeDescription instrumentedType, MethodDescription instrumentedMethod) { return new StackManipulation.Compound(stackManipulation, assigner.assign(typeDescription, fieldDescription.getType(), typing)); }
{@inheritDoc}
/** * {@inheritDoc} */
public Implementation andThen(Implementation implementation) { return new Compound(new OfConstantValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, typeDescription, stackManipulation), implementation); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable andThen(Composable implementation) { return new Compound.Composable(new OfConstantValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, typeDescription, stackManipulation), implementation); } }
An instrumentation that sets a field to a reference value that is stored in a static field of the instrumented type.
/** * An instrumentation that sets a field to a reference value that is stored in a static field of the instrumented type. */
@HashCodeAndEqualsPlugin.Enhance protected static class OfReferenceValue extends ForSetter<FieldDescription.InDefinedShape> {
The prefix used for implicitly named cached fields.
/** * The prefix used for implicitly named cached fields. */
protected static final String PREFIX = "fixedFieldValue";
The value to store.
/** * The value to store. */
private final Object value;
The name of the field to store the reference in.
/** * The name of the field to store the reference in. */
private final String name;
Creates a setter instrumentation for setting a value stored in a static field of the instrumented type.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
  • terminationHandler – The termination handler to apply.
  • value – The value to store.
  • name – The name of the field to store the reference in.
/** * Creates a setter instrumentation for setting a value stored in a static field of the instrumented type. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. * @param terminationHandler The termination handler to apply. * @param value The value to store. * @param name The name of the field to store the reference in. */
protected OfReferenceValue(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler, Object value, String name) { super(fieldLocation, assigner, typing, terminationHandler); this.value = value; this.name = name; }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType .withField(new FieldDescription.Token(name, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, TypeDescription.ForLoadedType.of(value.getClass()).asGenericType())) .withInitializer(new LoadedTypeInitializer.ForStaticField(name, value)); }
{@inheritDoc}
/** * {@inheritDoc} */
protected FieldDescription.InDefinedShape initialize(TypeDescription instrumentedType) { return instrumentedType.getDeclaredFields().filter(named(name)).getOnly(); }
{@inheritDoc}
/** * {@inheritDoc} */
protected StackManipulation resolve(FieldDescription.InDefinedShape target, FieldDescription fieldDescription, TypeDescription instrumentedType, MethodDescription instrumentedMethod) { if (fieldDescription.isFinal() && instrumentedMethod.isMethod()) { throw new IllegalArgumentException("Cannot set final field " + fieldDescription + " from " + instrumentedMethod); } return new StackManipulation.Compound( FieldAccess.forField(target).read(), assigner.assign(TypeDescription.ForLoadedType.of(value.getClass()).asGenericType(), fieldDescription.getType(), typing) ); }
{@inheritDoc}
/** * {@inheritDoc} */
public Implementation andThen(Implementation implementation) { return new Compound(new OfReferenceValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, value, name), implementation); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable andThen(Composable implementation) { return new Compound.Composable(new OfReferenceValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, value, name), implementation); } }
A setter that reads a value of another field and sets this value.
/** * A setter that reads a value of another field and sets this value. */
@HashCodeAndEqualsPlugin.Enhance protected static class OfFieldValue extends ForSetter<FieldLocation.Prepared> {
The target field locator.
/** * The target field locator. */
private final FieldLocation target;
Creates a setter that sets another field value.
Params:
  • fieldLocation – The field's location.
  • assigner – The assigner to use.
  • typing – Indicates if dynamic type castings should be attempted for incompatible assignments.
  • terminationHandler – The termination handler to apply.
  • target – The target field locator.
/** * Creates a setter that sets another field value. * * @param fieldLocation The field's location. * @param assigner The assigner to use. * @param typing Indicates if dynamic type castings should be attempted for incompatible assignments. * @param terminationHandler The termination handler to apply. * @param target The target field locator. */
protected OfFieldValue(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler, FieldLocation target) { super(fieldLocation, assigner, typing, terminationHandler); this.target = target; }
{@inheritDoc}
/** * {@inheritDoc} */
public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; }
{@inheritDoc}
/** * {@inheritDoc} */
protected FieldLocation.Prepared initialize(TypeDescription instrumentedType) { return target.prepare(instrumentedType); }
{@inheritDoc}
/** * {@inheritDoc} */
protected StackManipulation resolve(FieldLocation.Prepared target, FieldDescription fieldDescription, TypeDescription instrumentedType, MethodDescription instrumentedMethod) { FieldDescription resolved = target.resolve(instrumentedMethod); if (!resolved.isStatic() && instrumentedMethod.isStatic()) { throw new IllegalStateException("Cannot set instance field " + fieldDescription + " from " + instrumentedMethod); } return new StackManipulation.Compound( resolved.isStatic() ? StackManipulation.Trivial.INSTANCE : MethodVariableAccess.loadThis(), FieldAccess.forField(resolved).read(), assigner.assign(resolved.getType(), fieldDescription.getType(), typing) ); }
{@inheritDoc}
/** * {@inheritDoc} */
public Implementation andThen(Implementation implementation) { return new Compound(new OfFieldValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, target), implementation); }
{@inheritDoc}
/** * {@inheritDoc} */
public Composable andThen(Composable implementation) { return new Compound.Composable(new OfFieldValue(fieldLocation, assigner, typing, TerminationHandler.NON_OPERATIONAL, target), implementation); } }
An appender to implement a field setter.
/** * An appender to implement a field setter. */
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true) protected class Appender implements ByteCodeAppender {
The instrumented type.
/** * The instrumented type. */
private final TypeDescription instrumentedType;
The initialized value.
/** * The initialized value. */
private final T initialized;
The set field's prepared location.
/** * The set field's prepared location. */
private final FieldLocation.Prepared fieldLocation;
Creates a new appender for a field setter.
Params:
  • instrumentedType – The instrumented type.
  • initialized – The initialized value.
  • fieldLocation – The set field's prepared location.
/** * Creates a new appender for a field setter. * * @param instrumentedType The instrumented type. * @param initialized The initialized value. * @param fieldLocation The set field's prepared location. */
protected Appender(TypeDescription instrumentedType, T initialized, FieldLocation.Prepared fieldLocation) { this.instrumentedType = instrumentedType; this.initialized = initialized; this.fieldLocation = fieldLocation; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { FieldDescription fieldDescription = fieldLocation.resolve(instrumentedMethod); if (!fieldDescription.isStatic() && instrumentedMethod.isStatic()) { throw new IllegalStateException("Cannot set instance field " + fieldDescription + " from " + instrumentedMethod); } else if (fieldDescription.isFinal() && instrumentedMethod.isMethod()) { throw new IllegalStateException("Cannot set final field " + fieldDescription + " from " + instrumentedMethod); } StackManipulation stackManipulation = resolve(initialized, fieldDescription, instrumentedType, instrumentedMethod); if (!stackManipulation.isValid()) { throw new IllegalStateException("Set value cannot be assigned to " + fieldDescription); } return new Size(new StackManipulation.Compound( instrumentedMethod.isStatic() ? StackManipulation.Trivial.INSTANCE : MethodVariableAccess.loadThis(), stackManipulation, FieldAccess.forField(fieldDescription).write(), terminationHandler.resolve(instrumentedMethod) ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); } } } }