/*
 * 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.asm;

import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.ByteCodeElement;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.Duplication;
import net.bytebuddy.implementation.bytecode.Removal;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.StackSize;
import net.bytebuddy.implementation.bytecode.constant.DefaultValue;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import net.bytebuddy.utility.CompoundList;
import net.bytebuddy.utility.OpenedClassReader;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

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

Substitutes field access or method invocations within a method's body.

Important: This component relies on using a TypePool for locating types within method bodies. Within a redefinition or a rebasement, this type pool normally resolved correctly by Byte Buddy. When subclassing a type, the type pool must be set explicitly, using Builder.make(TypePool) or any similar method. It is however not normally necessary to use this component when subclassing a type where methods are only defined explicitly.

/** * <p> * Substitutes field access or method invocations within a method's body. * </p> * <p> * <b>Important</b>: This component relies on using a {@link TypePool} for locating types within method bodies. Within a redefinition * or a rebasement, this type pool normally resolved correctly by Byte Buddy. When subclassing a type, the type pool must be set * explicitly, using {@link net.bytebuddy.dynamic.DynamicType.Builder#make(TypePool)} or any similar method. It is however not normally * necessary to use this component when subclassing a type where methods are only defined explicitly. * </p> */
@HashCodeAndEqualsPlugin.Enhance public class MemberSubstitution implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
The method graph compiler to use.
/** * The method graph compiler to use. */
private final MethodGraph.Compiler methodGraphCompiler;
true if the method processing should be strict where an exception is raised if a member cannot be found.
/** * {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. */
private final boolean strict;
The type pool resolver to use.
/** * The type pool resolver to use. */
private final TypePoolResolver typePoolResolver;
The replacement factory to use.
/** * The replacement factory to use. */
private final Replacement.Factory replacementFactory;
Creates a default member substitution.
Params:
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
/** * Creates a default member substitution. * * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. */
protected MemberSubstitution(boolean strict) { this(MethodGraph.Compiler.DEFAULT, TypePoolResolver.OfImplicitPool.INSTANCE, strict, Replacement.NoOp.INSTANCE); }
Creates a new member substitution.
Params:
  • methodGraphCompiler – The method graph compiler to use.
  • typePoolResolver – The type pool resolver to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacementFactory – The replacement factory to use.
/** * Creates a new member substitution. * * @param methodGraphCompiler The method graph compiler to use. * @param typePoolResolver The type pool resolver to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacementFactory The replacement factory to use. */
protected MemberSubstitution(MethodGraph.Compiler methodGraphCompiler, TypePoolResolver typePoolResolver, boolean strict, Replacement.Factory replacementFactory) { this.methodGraphCompiler = methodGraphCompiler; this.typePoolResolver = typePoolResolver; this.strict = strict; this.replacementFactory = replacementFactory; }
Creates a member substitution that requires the resolution of all fields and methods that are referenced within a method body. Doing so, this component raises an exception if any member cannot be resolved what makes this component unusable when facing optional types.
Returns:A strict member substitution.
/** * Creates a member substitution that requires the resolution of all fields and methods that are referenced within a method body. Doing so, * this component raises an exception if any member cannot be resolved what makes this component unusable when facing optional types. * * @return A strict member substitution. */
public static MemberSubstitution strict() { return new MemberSubstitution(true); }
Creates a member substitution that skips any unresolvable fields or methods that are referenced within a method body. Using a relaxed member substitution, methods containing optional types are supported. In the process, it is however possible that misconfigurations of this component remain undiscovered.
Returns:A relaxed member substitution.
/** * Creates a member substitution that skips any unresolvable fields or methods that are referenced within a method body. Using a relaxed * member substitution, methods containing optional types are supported. In the process, it is however possible that misconfigurations * of this component remain undiscovered. * * @return A relaxed member substitution. */
public static MemberSubstitution relaxed() { return new MemberSubstitution(false); }
Substitutes any interaction with a field or method that matches the given matcher.
Params:
  • matcher – The matcher to determine what access to byte code elements to substitute.
Returns:A specification that allows to determine how to substitute any interaction with byte code elements that match the supplied matcher.
/** * Substitutes any interaction with a field or method that matches the given matcher. * * @param matcher The matcher to determine what access to byte code elements to substitute. * @return A specification that allows to determine how to substitute any interaction with byte code elements that match the supplied matcher. */
public WithoutSpecification element(ElementMatcher<? super ByteCodeElement> matcher) { return new WithoutSpecification.ForMatchedByteCodeElement(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher); }
Substitutes any field access that matches the given matcher.
Params:
  • matcher – The matcher to determine what fields to substitute.
Returns:A specification that allows to determine how to substitute any field access that match the supplied matcher.
/** * Substitutes any field access that matches the given matcher. * * @param matcher The matcher to determine what fields to substitute. * @return A specification that allows to determine how to substitute any field access that match the supplied matcher. */
public WithoutSpecification.ForMatchedField field(ElementMatcher<? super FieldDescription.InDefinedShape> matcher) { return new WithoutSpecification.ForMatchedField(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher); }
Substitutes any method invocation that matches the given matcher.
Params:
  • matcher – The matcher to determine what methods to substitute.
Returns:A specification that allows to determine how to substitute any method invocations that match the supplied matcher.
/** * Substitutes any method invocation that matches the given matcher. * * @param matcher The matcher to determine what methods to substitute. * @return A specification that allows to determine how to substitute any method invocations that match the supplied matcher. */
public WithoutSpecification.ForMatchedMethod method(ElementMatcher<? super MethodDescription> matcher) { return new WithoutSpecification.ForMatchedMethod(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher); }
Substitutes any constructor invocation that matches the given matcher.
Params:
  • matcher – The matcher to determine what constructors to substitute.
Returns:A specification that allows to determine how to substitute any constructor invocations that match the supplied matcher.
/** * Substitutes any constructor invocation that matches the given matcher. * * @param matcher The matcher to determine what constructors to substitute. * @return A specification that allows to determine how to substitute any constructor invocations that match the supplied matcher. */
public WithoutSpecification constructor(ElementMatcher<? super MethodDescription> matcher) { return invokable(isConstructor().and(matcher)); }
Substitutes any method or constructor invocation that matches the given matcher.
Params:
  • matcher – The matcher to determine what method or constructors to substitute.
Returns:A specification that allows to determine how to substitute any constructor invocations that match the supplied matcher.
/** * Substitutes any method or constructor invocation that matches the given matcher. * * @param matcher The matcher to determine what method or constructors to substitute. * @return A specification that allows to determine how to substitute any constructor invocations that match the supplied matcher. */
public WithoutSpecification invokable(ElementMatcher<? super MethodDescription> matcher) { return new WithoutSpecification.ForMatchedMethod(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher); }
Specifies the use of a specific method graph compiler for the resolution of virtual methods.
Params:
  • methodGraphCompiler – The method graph compiler to use.
Returns:A new member substitution that is equal to this but uses the specified method graph compiler.
/** * Specifies the use of a specific method graph compiler for the resolution of virtual methods. * * @param methodGraphCompiler The method graph compiler to use. * @return A new member substitution that is equal to this but uses the specified method graph compiler. */
public MemberSubstitution with(MethodGraph.Compiler methodGraphCompiler) { return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, replacementFactory); }
Specifies a type pool resolver to be used for locating members.
Params:
  • typePoolResolver – The type pool resolver to use.
Returns:A new instance of this member substitution that uses the supplied type pool resolver.
/** * Specifies a type pool resolver to be used for locating members. * * @param typePoolResolver The type pool resolver to use. * @return A new instance of this member substitution that uses the supplied type pool resolver. */
public MemberSubstitution with(TypePoolResolver typePoolResolver) { return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, replacementFactory); }
Applies this member substitution to any method that matches the supplied matcher.
Params:
  • matcher – The matcher to determine this substitutions application.
Returns:An ASM visitor wrapper that applies all specified substitutions for any matched method.
/** * Applies this member substitution to any method that matches the supplied matcher. * * @param matcher The matcher to determine this substitutions application. * @return An ASM visitor wrapper that applies all specified substitutions for any matched method. */
public AsmVisitorWrapper.ForDeclaredMethods on(ElementMatcher<? super MethodDescription> matcher) { return new AsmVisitorWrapper.ForDeclaredMethods().invokable(matcher, this); }
{@inheritDoc}
/** * {@inheritDoc} */
public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription instrumentedMethod, MethodVisitor methodVisitor, Implementation.Context implementationContext, TypePool typePool, int writerFlags, int readerFlags) { typePool = typePoolResolver.resolve(instrumentedType, instrumentedMethod, typePool); return new SubstitutingMethodVisitor(methodVisitor, instrumentedType, methodGraphCompiler, strict, replacementFactory.make(instrumentedType, instrumentedMethod, typePool), implementationContext, typePool); }
A member substitution that lacks a specification for how to substitute the matched members references within a method body.
/** * A member substitution that lacks a specification for how to substitute the matched members references within a method body. */
@HashCodeAndEqualsPlugin.Enhance public abstract static class WithoutSpecification {
The method graph compiler to use.
/** * The method graph compiler to use. */
protected final MethodGraph.Compiler methodGraphCompiler;
The type pool resolver to use.
/** * The type pool resolver to use. */
protected final TypePoolResolver typePoolResolver;
true if the method processing should be strict where an exception is raised if a member cannot be found.
/** * {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. */
protected final boolean strict;
The replacement factory to use for creating substitutions.
/** * The replacement factory to use for creating substitutions. */
protected final Replacement.Factory replacementFactory;
Creates a new member substitution that requires a specification for how to perform a substitution.
Params:
  • methodGraphCompiler – The method graph compiler to use.
  • typePoolResolver – The type pool resolver to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacementFactory – The replacement factory to use for creating substitutions.
/** * Creates a new member substitution that requires a specification for how to perform a substitution. * * @param methodGraphCompiler The method graph compiler to use. * @param typePoolResolver The type pool resolver to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacementFactory The replacement factory to use for creating substitutions. */
protected WithoutSpecification(MethodGraph.Compiler methodGraphCompiler, TypePoolResolver typePoolResolver, boolean strict, Replacement.Factory replacementFactory) { this.methodGraphCompiler = methodGraphCompiler; this.typePoolResolver = typePoolResolver; this.strict = strict; this.replacementFactory = replacementFactory; }
Subs any interaction with a matched byte code element. Any value read from the element will be replaced with the stubbed value's default, i.e. null for reference types and the specific 0 value for primitive types. Any written value will simply be discarded.
Returns:A member substitution that stubs any interaction with a matched byte code element.
/** * Subs any interaction with a matched byte code element. Any value read from the element will be replaced with the stubbed * value's default, i.e. {@code null} for reference types and the specific {@code 0} value for primitive types. Any written * value will simply be discarded. * * @return A member substitution that stubs any interaction with a matched byte code element. */
public MemberSubstitution stub() { return replaceWith(Substitution.Stubbing.INSTANCE); }

Replaces any interaction with a matched byte code element by an interaction with the specified field. If a field is replacing a method or constructor invocation, it is treated as if it was a field getter or setter respectively.

A replacement can only be applied if the field is compatible to the original byte code element, i.e. consumes an instance of the declaring type if it is not static as an argument and consumes or produces an instance of the field's type.

Params:
  • field – The field to access instead of interacting with any of the matched byte code elements.
Returns:A member substitution that replaces any matched byte code element with an access of the specified field.
/** * <p> * Replaces any interaction with a matched byte code element by an interaction with the specified field. If a field * is replacing a method or constructor invocation, it is treated as if it was a field getter or setter respectively. * </p> * <p> * A replacement can only be applied if the field is compatible to the original byte code element, i.e. consumes an * instance of the declaring type if it is not {@code static} as an argument and consumes or produces an instance of * the field's type. * </p> * * @param field The field to access instead of interacting with any of the matched byte code elements. * @return A member substitution that replaces any matched byte code element with an access of the specified field. */
public MemberSubstitution replaceWith(Field field) { return replaceWith(new FieldDescription.ForLoadedField(field)); }

Replaces any interaction with a matched byte code element by an interaction with the specified field. If a field is replacing a method or constructor invocation, it is treated as if it was a field getter or setter respectively.

A replacement can only be applied if the field is compatible to the original byte code element, i.e. consumes an instance of the declaring type if it is not static as an argument and consumes or produces an instance of the field's type.

Params:
  • fieldDescription – The field to access instead of interacting with any of the matched byte code elements.
Returns:A member substitution that replaces any matched byte code element with an access of the specified field.
/** * <p> * Replaces any interaction with a matched byte code element by an interaction with the specified field. If a field * is replacing a method or constructor invocation, it is treated as if it was a field getter or setter respectively. * </p> * <p> * A replacement can only be applied if the field is compatible to the original byte code element, i.e. consumes an * instance of the declaring type if it is not {@code static} as an argument and consumes or produces an instance of * the field's type. * </p> * * @param fieldDescription The field to access instead of interacting with any of the matched byte code elements. * @return A member substitution that replaces any matched byte code element with an access of the specified field. */
public MemberSubstitution replaceWith(FieldDescription fieldDescription) { return replaceWith(new Substitution.ForFieldAccess.OfGivenField(fieldDescription)); }
Replaces any interaction with a matched byte code element with a non-static field access on the first parameter of the matched element. When matching a non-static field access or method invocation, the substituted field is located on the same receiver type as the original access. For static access, the first argument is used as a receiver.
Params:
  • matcher – A matcher for locating a field on the original interaction's receiver type.
Returns:A member substitution that replaces any matched byte code element with an access of the matched field.
/** * Replaces any interaction with a matched byte code element with a non-static field access on the first * parameter of the matched element. When matching a non-static field access or method invocation, the * substituted field is located on the same receiver type as the original access. For static access, the * first argument is used as a receiver. * * @param matcher A matcher for locating a field on the original interaction's receiver type. * @return A member substitution that replaces any matched byte code element with an access of the matched field. */
public MemberSubstitution replaceWithField(ElementMatcher<? super FieldDescription> matcher) { return replaceWith(new Substitution.ForFieldAccess.OfMatchedField(matcher)); }

Replaces any interaction with a matched byte code element by an invocation of the specified method. If a method is replacing a field access, it is treated as if it was replacing an invocation of the field's getter or setter respectively.

A replacement can only be applied if the method is compatible to the original byte code element, i.e. consumes compatible arguments and returns a compatible value. If the method is not static, it is treated as if this was an implicit first argument.

Params:
  • method – The method to invoke instead of interacting with any of the matched byte code elements.
Returns:A member substitution that replaces any matched byte code element with an invocation of the specified method.
/** * <p> * Replaces any interaction with a matched byte code element by an invocation of the specified method. If a method * is replacing a field access, it is treated as if it was replacing an invocation of the field's getter or setter respectively. * </p> * <p> * A replacement can only be applied if the method is compatible to the original byte code element, i.e. consumes compatible * arguments and returns a compatible value. If the method is not {@code static}, it is treated as if {@code this} was an implicit * first argument. * </p> * * @param method The method to invoke instead of interacting with any of the matched byte code elements. * @return A member substitution that replaces any matched byte code element with an invocation of the specified method. */
public MemberSubstitution replaceWith(Method method) { return replaceWith(new MethodDescription.ForLoadedMethod(method)); }

Replaces any interaction with a matched byte code element by an invocation of the specified method. If a method is replacing a field access, it is treated as if it was replacing an invocation of the field's getter or setter respectively.

A replacement can only be applied if the method is compatible to the original byte code element, i.e. consumes compatible arguments and returns a compatible value. If the method is not static, it is treated as if this was an implicit first argument.

Important: It is not allowed to specify a constructor or the static type initializer as a replacement.

Params:
  • methodDescription – The method to invoke instead of interacting with any of the matched byte code elements.
Returns:A member substitution that replaces any matched byte code element with an invocation of the specified method.
/** * <p> * Replaces any interaction with a matched byte code element by an invocation of the specified method. If a method * is replacing a field access, it is treated as if it was replacing an invocation of the field's getter or setter respectively. * </p> * <p> * A replacement can only be applied if the method is compatible to the original byte code element, i.e. consumes compatible * arguments and returns a compatible value. If the method is not {@code static}, it is treated as if {@code this} was an implicit * first argument. * </p> * <p> * <b>Important</b>: It is not allowed to specify a constructor or the static type initializer as a replacement. * </p> * * @param methodDescription The method to invoke instead of interacting with any of the matched byte code elements. * @return A member substitution that replaces any matched byte code element with an invocation of the specified method. */
public MemberSubstitution replaceWith(MethodDescription methodDescription) { if (!methodDescription.isMethod()) { throw new IllegalArgumentException("Cannot use " + methodDescription + " as a replacement"); } return replaceWith(new Substitution.ForMethodInvocation.OfGivenMethod(methodDescription)); }
Replaces any interaction with a matched byte code element with a non-static method access on the first parameter of the matched element. When matching a non-static field access or method invocation, the substituted method is located on the same receiver type as the original access. For static access, the first argument is used as a receiver.
Params:
  • matcher – A matcher for locating a method on the original interaction's receiver type.
Returns:A member substitution that replaces any matched byte code element with an access of the matched method.
/** * Replaces any interaction with a matched byte code element with a non-static method access on the first * parameter of the matched element. When matching a non-static field access or method invocation, the * substituted method is located on the same receiver type as the original access. For static access, the * first argument is used as a receiver. * * @param matcher A matcher for locating a method on the original interaction's receiver type. * @return A member substitution that replaces any matched byte code element with an access of the matched method. */
public MemberSubstitution replaceWithMethod(ElementMatcher<? super MethodDescription> matcher) { return replaceWithMethod(matcher, methodGraphCompiler); }
Replaces any interaction with a matched byte code element with a non-static method access on the first parameter of the matched element. When matching a non-static field access or method invocation, the substituted method is located on the same receiver type as the original access. For static access, the first argument is used as a receiver.
Params:
  • matcher – A matcher for locating a method on the original interaction's receiver type.
  • methodGraphCompiler – The method graph compiler to use for locating a method.
Returns:A member substitution that replaces any matched byte code element with an access of the matched method.
/** * Replaces any interaction with a matched byte code element with a non-static method access on the first * parameter of the matched element. When matching a non-static field access or method invocation, the * substituted method is located on the same receiver type as the original access. For static access, the * first argument is used as a receiver. * * @param matcher A matcher for locating a method on the original interaction's receiver type. * @param methodGraphCompiler The method graph compiler to use for locating a method. * @return A member substitution that replaces any matched byte code element with an access of the matched method. */
public MemberSubstitution replaceWithMethod(ElementMatcher<? super MethodDescription> matcher, MethodGraph.Compiler methodGraphCompiler) { return replaceWith(new Substitution.ForMethodInvocation.OfMatchedMethod(matcher, methodGraphCompiler)); }
Replaces any interaction with a matched byte code element with an invocation of the instrumented method. This can cause an infinite recursive call if the arguments to the method are not altered.
Returns:A member substitution that replaces any matched byte code element with an invocation of the instrumented method.
/** * Replaces any interaction with a matched byte code element with an invocation of the instrumented * method. This can cause an infinite recursive call if the arguments to the method are not altered. * * @return A member substitution that replaces any matched byte code element with an invocation of the * instrumented method. */
public MemberSubstitution replaceWithInstrumentedMethod() { return replaceWith(Substitution.ForMethodInvocation.OfInstrumentedMethod.INSTANCE); }
Replaces any interaction with the supplied substitution.
Params:
  • factory – The substitution factory to use for creating the applied substitution.
Returns:A member substitution that replaces any matched byte code element with the supplied substitution.
/** * Replaces any interaction with the supplied substitution. * * @param factory The substitution factory to use for creating the applied substitution. * @return A member substitution that replaces any matched byte code element with the supplied substitution. */
public abstract MemberSubstitution replaceWith(Substitution.Factory factory);
Describes a member substitution that requires a specification for how to replace a byte code element.
/** * Describes a member substitution that requires a specification for how to replace a byte code element. */
@HashCodeAndEqualsPlugin.Enhance protected static class ForMatchedByteCodeElement extends WithoutSpecification {
A matcher for any byte code elements that should be substituted.
/** * A matcher for any byte code elements that should be substituted. */
private final ElementMatcher<? super ByteCodeElement> matcher;
Creates a new member substitution for a matched byte code element that requires a specification for how to perform a substitution.
Params:
  • methodGraphCompiler – The method graph compiler to use.
  • typePoolResolver – The type pool resolver to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacementFactory – The replacement factory to use.
  • matcher – A matcher for any byte code elements that should be substituted.
/** * Creates a new member substitution for a matched byte code element that requires a specification for how to perform a substitution. * * @param methodGraphCompiler The method graph compiler to use. * @param typePoolResolver The type pool resolver to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacementFactory The replacement factory to use. * @param matcher A matcher for any byte code elements that should be substituted. */
protected ForMatchedByteCodeElement(MethodGraph.Compiler methodGraphCompiler, TypePoolResolver typePoolResolver, boolean strict, Replacement.Factory replacementFactory, ElementMatcher<? super ByteCodeElement> matcher) { super(methodGraphCompiler, typePoolResolver, strict, replacementFactory); this.matcher = matcher; }
{@inheritDoc}
/** * {@inheritDoc} */
public MemberSubstitution replaceWith(Substitution.Factory substitutionFactory) { return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, new Replacement.Factory.Compound( this.replacementFactory, Replacement.ForElementMatchers.Factory.of(matcher, substitutionFactory))); } }
Describes a member substitution that requires a specification for how to replace a field.
/** * Describes a member substitution that requires a specification for how to replace a field. */
@HashCodeAndEqualsPlugin.Enhance public static class ForMatchedField extends WithoutSpecification {
A matcher for any field that should be substituted.
/** * A matcher for any field that should be substituted. */
private final ElementMatcher<? super FieldDescription.InDefinedShape> matcher;
true if read access to a field should be substituted.
/** * {@code true} if read access to a field should be substituted. */
private final boolean matchRead;
true if write access to a field should be substituted.
/** * {@code true} if write access to a field should be substituted. */
private final boolean matchWrite;
Creates a new member substitution for a matched field that requires a specification for how to perform a substitution.
Params:
  • methodGraphCompiler – The method graph compiler to use.
  • typePoolResolver – The type pool resolver to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacementFactory – The replacement factory to use.
  • matcher – A matcher for any field that should be substituted.
/** * Creates a new member substitution for a matched field that requires a specification for how to perform a substitution. * * @param methodGraphCompiler The method graph compiler to use. * @param typePoolResolver The type pool resolver to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacementFactory The replacement factory to use. * @param matcher A matcher for any field that should be substituted. */
protected ForMatchedField(MethodGraph.Compiler methodGraphCompiler, TypePoolResolver typePoolResolver, boolean strict, Replacement.Factory replacementFactory, ElementMatcher<? super FieldDescription.InDefinedShape> matcher) { this(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher, true, true); }
Creates a new member substitution for a matched field that requires a specification for how to perform a substitution.
Params:
  • methodGraphCompiler – The method graph compiler to use.
  • typePoolResolver – The type pool resolver to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacementFactory – The replacement factory to use.
  • matcher – A matcher for any field that should be substituted.
  • matchRead – true if read access to a field should be substituted.
  • matchWrite – true if write access to a field should be substituted.
/** * Creates a new member substitution for a matched field that requires a specification for how to perform a substitution. * * @param methodGraphCompiler The method graph compiler to use. * @param typePoolResolver The type pool resolver to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacementFactory The replacement factory to use. * @param matcher A matcher for any field that should be substituted. * @param matchRead {@code true} if read access to a field should be substituted. * @param matchWrite {@code true} if write access to a field should be substituted. */
protected ForMatchedField(MethodGraph.Compiler methodGraphCompiler, TypePoolResolver typePoolResolver, boolean strict, Replacement.Factory replacementFactory, ElementMatcher<? super FieldDescription.InDefinedShape> matcher, boolean matchRead, boolean matchWrite) { super(methodGraphCompiler, typePoolResolver, strict, replacementFactory); this.matcher = matcher; this.matchRead = matchRead; this.matchWrite = matchWrite; }
When invoked, only read access of the previously matched field is substituted.
Returns:This instance with the limitation that only read access to the matched field is substituted.
/** * When invoked, only read access of the previously matched field is substituted. * * @return This instance with the limitation that only read access to the matched field is substituted. */
public WithoutSpecification onRead() { return new ForMatchedField(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher, true, false); }
When invoked, only write access of the previously matched field is substituted.
Returns:This instance with the limitation that only write access to the matched field is substituted.
/** * When invoked, only write access of the previously matched field is substituted. * * @return This instance with the limitation that only write access to the matched field is substituted. */
public WithoutSpecification onWrite() { return new ForMatchedField(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher, false, true); }
{@inheritDoc}
/** * {@inheritDoc} */
public MemberSubstitution replaceWith(Substitution.Factory substitutionFactory) { return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, new Replacement.Factory.Compound( this.replacementFactory, Replacement.ForElementMatchers.Factory.ofField(matcher, matchRead, matchWrite, substitutionFactory))); } }
Describes a member substitution that requires a specification for how to replace a method or constructor.
/** * Describes a member substitution that requires a specification for how to replace a method or constructor. */
@HashCodeAndEqualsPlugin.Enhance public static class ForMatchedMethod extends WithoutSpecification {
A matcher for any method or constructor that should be substituted.
/** * A matcher for any method or constructor that should be substituted. */
private final ElementMatcher<? super MethodDescription> matcher;
true if this specification includes virtual invocations.
/** * {@code true} if this specification includes virtual invocations. */
private final boolean includeVirtualCalls;
true if this specification includes super invocations.
/** * {@code true} if this specification includes {@code super} invocations. */
private final boolean includeSuperCalls;
Creates a new member substitution for a matched method that requires a specification for how to perform a substitution.
Params:
  • methodGraphCompiler – The method graph compiler to use.
  • typePoolResolver – The type pool resolver to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacementFactory – The replacement factory to use.
  • matcher – A matcher for any method or constructor that should be substituted.
/** * Creates a new member substitution for a matched method that requires a specification for how to perform a substitution. * * @param methodGraphCompiler The method graph compiler to use. * @param typePoolResolver The type pool resolver to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacementFactory The replacement factory to use. * @param matcher A matcher for any method or constructor that should be substituted. */
protected ForMatchedMethod(MethodGraph.Compiler methodGraphCompiler, TypePoolResolver typePoolResolver, boolean strict, Replacement.Factory replacementFactory, ElementMatcher<? super MethodDescription> matcher) { this(methodGraphCompiler, typePoolResolver, strict, replacementFactory, matcher, true, true); }
Creates a new member substitution for a matched method that requires a specification for how to perform a substitution.
Params:
  • methodGraphCompiler – The method graph compiler to use.
  • typePoolResolver – The type pool resolver to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacementFactory – The replacement factory to use.
  • matcher – A matcher for any method or constructor that should be substituted.
  • includeVirtualCalls – true if this specification includes virtual invocations.
  • includeSuperCalls – true if this specification includes super invocations.
/** * Creates a new member substitution for a matched method that requires a specification for how to perform a substitution. * * @param methodGraphCompiler The method graph compiler to use. * @param typePoolResolver The type pool resolver to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacementFactory The replacement factory to use. * @param matcher A matcher for any method or constructor that should be substituted. * @param includeVirtualCalls {@code true} if this specification includes virtual invocations. * @param includeSuperCalls {@code true} if this specification includes {@code super} invocations. */
protected ForMatchedMethod(MethodGraph.Compiler methodGraphCompiler, TypePoolResolver typePoolResolver, boolean strict, Replacement.Factory replacementFactory, ElementMatcher<? super MethodDescription> matcher, boolean includeVirtualCalls, boolean includeSuperCalls) { super(methodGraphCompiler, typePoolResolver, strict, replacementFactory); this.matcher = matcher; this.includeVirtualCalls = includeVirtualCalls; this.includeSuperCalls = includeSuperCalls; }
Limits the substituted method calls to method calls that invoke a method virtually (as opposed to a super invocation).
Returns:This specification where only virtual methods are matched if they are not invoked as a virtual call.
/** * Limits the substituted method calls to method calls that invoke a method virtually (as opposed to a {@code super} invocation). * * @return This specification where only virtual methods are matched if they are not invoked as a virtual call. */
public WithoutSpecification onVirtualCall() { return new ForMatchedMethod(methodGraphCompiler, typePoolResolver, strict, replacementFactory, isVirtual().and(matcher), true, false); }
Limits the substituted method calls to method calls that invoke a method as a super call.
Returns:This specification where only virtual methods are matched if they are not invoked as a super call.
/** * Limits the substituted method calls to method calls that invoke a method as a {@code super} call. * * @return This specification where only virtual methods are matched if they are not invoked as a super call. */
public WithoutSpecification onSuperCall() { return new ForMatchedMethod(methodGraphCompiler, typePoolResolver, strict, replacementFactory, isVirtual().and(matcher), false, true); }
{@inheritDoc}
/** * {@inheritDoc} */
public MemberSubstitution replaceWith(Substitution.Factory substitutionFactory) { return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, new Replacement.Factory.Compound( this.replacementFactory, Replacement.ForElementMatchers.Factory.ofMethod(matcher, includeVirtualCalls, includeSuperCalls, substitutionFactory))); } } }
A type pool resolver is responsible for resolving a TypePool for locating substituted members.
/** * A type pool resolver is responsible for resolving a {@link TypePool} for locating substituted members. */
public interface TypePoolResolver {
Resolves a type pool to use for locating substituted members.
Params:
  • instrumentedType – The instrumented type.
  • instrumentedMethod – The instrumented method.
  • typePool – The type pool implicit to the instrumentation.
Returns:The type pool to use.
/** * Resolves a type pool to use for locating substituted members. * * @param instrumentedType The instrumented type. * @param instrumentedMethod The instrumented method. * @param typePool The type pool implicit to the instrumentation. * @return The type pool to use. */
TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool);
Returns the implicit type pool.
/** * Returns the implicit type pool. */
enum OfImplicitPool implements TypePoolResolver {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return typePool; } }
A type pool resolver that returns a specific type pool.
/** * A type pool resolver that returns a specific type pool. */
@HashCodeAndEqualsPlugin.Enhance class ForExplicitPool implements TypePoolResolver {
The type pool to return.
/** * The type pool to return. */
private final TypePool typePool;
Creates a resolver for an explicit type pool.
Params:
  • typePool – The type pool to return.
/** * Creates a resolver for an explicit type pool. * * @param typePool The type pool to return. */
public ForExplicitPool(TypePool typePool) { this.typePool = typePool; }
{@inheritDoc}
/** * {@inheritDoc} */
public TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return this.typePool; } }
A type pool resolver that resolves the implicit pool but additionally checks another class file locator.
/** * A type pool resolver that resolves the implicit pool but additionally checks another class file locator. */
@HashCodeAndEqualsPlugin.Enhance class ForClassFileLocator implements TypePoolResolver {
The class file locator to use.
/** * The class file locator to use. */
private final ClassFileLocator classFileLocator;
The reader mode to apply.
/** * The reader mode to apply. */
private final TypePool.Default.ReaderMode readerMode;
Creates a new type pool resolver for a class file locator as a supplement of the implicit type pool.
Params:
  • classFileLocator – The class file locator to use.
/** * Creates a new type pool resolver for a class file locator as a supplement of the implicit type pool. * * @param classFileLocator The class file locator to use. */
public ForClassFileLocator(ClassFileLocator classFileLocator) { this(classFileLocator, TypePool.Default.ReaderMode.FAST); }
Creates a new type pool resolver for a class file locator as a supplement of the implicit type pool.
Params:
  • classFileLocator – The class file locator to use.
  • readerMode – The reader mode to apply.
/** * Creates a new type pool resolver for a class file locator as a supplement of the implicit type pool. * * @param classFileLocator The class file locator to use. * @param readerMode The reader mode to apply. */
public ForClassFileLocator(ClassFileLocator classFileLocator, TypePool.Default.ReaderMode readerMode) { this.classFileLocator = classFileLocator; this.readerMode = readerMode; }
Creates a new type pool resolver that supplements the supplied class loader to the implicit type pool.
Params:
  • classLoader – The class loader to use as a supplement which can be null to represent the bootstrap loader.
Returns:An appropriate type pool resolver.
/** * Creates a new type pool resolver that supplements the supplied class loader to the implicit type pool. * * @param classLoader The class loader to use as a supplement which can be {@code null} to represent the bootstrap loader. * @return An appropriate type pool resolver. */
public static TypePoolResolver of(ClassLoader classLoader) { return new ForClassFileLocator(ClassFileLocator.ForClassLoader.of(classLoader)); }
{@inheritDoc}
/** * {@inheritDoc} */
public TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return new TypePool.Default(new TypePool.CacheProvider.Simple(), classFileLocator, readerMode, typePool); } } }
A substitution replaces or enhances an interaction with a field or method within an instrumented method.
/** * A substitution replaces or enhances an interaction with a field or method within an instrumented method. */
public interface Substitution {
Resolves this substitution into a stack manipulation.
Params:
  • targetType – The target type on which a member is accessed.
  • target – The target field, method or constructor that is substituted,
  • parameters – All parameters that serve as input to this access.
  • result – The result that is expected from the interaction or void if no result is expected.
Returns:A stack manipulation that represents the access.
/** * Resolves this substitution into a stack manipulation. * * @param targetType The target type on which a member is accessed. * @param target The target field, method or constructor that is substituted, * @param parameters All parameters that serve as input to this access. * @param result The result that is expected from the interaction or {@code void} if no result is expected. * @return A stack manipulation that represents the access. */
StackManipulation resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result);
A factory for creating a substitution for an instrumented method.
/** * A factory for creating a substitution for an instrumented method. */
interface Factory {
Creates a substitution for an instrumented method.
Params:
  • instrumentedType – The instrumented type.
  • instrumentedMethod – The instrumented method.
  • typePool – The type pool being used.
Returns:The substitution to apply within the instrumented method.
/** * Creates a substitution for an instrumented method. * * @param instrumentedType The instrumented type. * @param instrumentedMethod The instrumented method. * @param typePool The type pool being used. * @return The substitution to apply within the instrumented method. */
Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool); }
A substitution that drops any field or method access and returns the expected return type's default value, i.e null or zero for primitive types.
/** * A substitution that drops any field or method access and returns the expected return type's default value, i.e {@code null} or zero for primitive types. */
enum Stubbing implements Substitution, Factory {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(parameters.size()); for (int index = parameters.size() - 1; index >= 0; index--) { stackManipulations.add(Removal.of(parameters.get(index))); } return new StackManipulation.Compound(CompoundList.of(stackManipulations, DefaultValue.of(result.asErasure()))); } }
A substitution with a field access.
/** * A substitution with a field access. */
@HashCodeAndEqualsPlugin.Enhance class ForFieldAccess implements Substitution {
The instrumented type.
/** * The instrumented type. */
private final TypeDescription instrumentedType;
A resolver to locate the field to access.
/** * A resolver to locate the field to access. */
private final FieldResolver fieldResolver;
Creates a new substitution with a field access.
Params:
  • instrumentedType – The instrumented type.
  • fieldResolver – A resolver to locate the field to access.
/** * Creates a new substitution with a field access. * * @param instrumentedType The instrumented type. * @param fieldResolver A resolver to locate the field to access. */
public ForFieldAccess(TypeDescription instrumentedType, FieldResolver fieldResolver) { this.instrumentedType = instrumentedType; this.fieldResolver = fieldResolver; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { FieldDescription fieldDescription = fieldResolver.resolve(targetType, target, parameters, result); if (!fieldDescription.isAccessibleTo(instrumentedType)) { throw new IllegalStateException(instrumentedType + " cannot access " + fieldDescription); } else if (result.represents(void.class)) { if (parameters.size() != (fieldDescription.isStatic() ? 1 : 2)) { throw new IllegalStateException("Cannot set " + fieldDescription + " with " + parameters); } else if (!fieldDescription.isStatic() && !parameters.get(0).asErasure().isAssignableTo(fieldDescription.getDeclaringType().asErasure())) { throw new IllegalStateException("Cannot set " + fieldDescription + " on " + parameters.get(0)); } else if (!parameters.get(fieldDescription.isStatic() ? 0 : 1).asErasure().isAssignableTo(fieldDescription.getType().asErasure())) { throw new IllegalStateException("Cannot set " + fieldDescription + " to " + parameters.get(fieldDescription.isStatic() ? 0 : 1)); } return FieldAccess.forField(fieldDescription).write(); } else { if (parameters.size() != (fieldDescription.isStatic() ? 0 : 1)) { throw new IllegalStateException("Cannot set " + fieldDescription + " with " + parameters); } else if (!fieldDescription.isStatic() && !parameters.get(0).asErasure().isAssignableTo(fieldDescription.getDeclaringType().asErasure())) { throw new IllegalStateException("Cannot get " + fieldDescription + " on " + parameters.get(0)); } else if (!fieldDescription.getType().asErasure().isAssignableTo(result.asErasure())) { throw new IllegalStateException("Cannot get " + fieldDescription + " as " + result); } return FieldAccess.forField(fieldDescription).read(); } }
A method resolver for locating a field for a substitute.
/** * A method resolver for locating a field for a substitute. */
public interface FieldResolver {
Resolves the field to substitute with.
Params:
  • targetType – The target type on which a member is accessed.
  • target – The target field, method or constructor that is substituted,
  • parameters – All parameters that serve as input to this access.
  • result – The result that is expected from the interaction or void if no result is expected.
Returns:The field to substitute with.
/** * Resolves the field to substitute with. * * @param targetType The target type on which a member is accessed. * @param target The target field, method or constructor that is substituted, * @param parameters All parameters that serve as input to this access. * @param result The result that is expected from the interaction or {@code void} if no result is expected. * @return The field to substitute with. */
FieldDescription resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result);
A simple field resolver that returns a specific field.
/** * A simple field resolver that returns a specific field. */
@HashCodeAndEqualsPlugin.Enhance class Simple implements FieldResolver {
The field to access.
/** * The field to access. */
private final FieldDescription fieldDescription;
Creates a simple field resolver.
Params:
  • fieldDescription – The field to access.
/** * Creates a simple field resolver. * * @param fieldDescription The field to access. */
public Simple(FieldDescription fieldDescription) { this.fieldDescription = fieldDescription; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldDescription resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { return fieldDescription; } }
A field matcher that resolves a non-static field on the first parameter type of the substituted member usage.
/** * A field matcher that resolves a non-static field on the first parameter type of the substituted member usage. */
@HashCodeAndEqualsPlugin.Enhance class ForElementMatcher implements FieldResolver {
The instrumented type.
/** * The instrumented type. */
private final TypeDescription instrumentedType;
The matcher to use for locating the field to substitute with.
/** * The matcher to use for locating the field to substitute with. */
private final ElementMatcher<? super FieldDescription> matcher;
Creates a new field resolver that locates a field on the receiver type using a matcher.
Params:
  • instrumentedType – The instrumented type.
  • matcher – The matcher to use for locating the field to substitute with.
/** * Creates a new field resolver that locates a field on the receiver type using a matcher. * * @param instrumentedType The instrumented type. * @param matcher The matcher to use for locating the field to substitute with. */
protected ForElementMatcher(TypeDescription instrumentedType, ElementMatcher<? super FieldDescription> matcher) { this.instrumentedType = instrumentedType; this.matcher = matcher; }
{@inheritDoc}
/** * {@inheritDoc} */
public FieldDescription resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { if (parameters.isEmpty()) { throw new IllegalStateException("Cannot substitute parameterless instruction with " + target); } else if (parameters.get(0).isPrimitive() || parameters.get(0).isArray()) { throw new IllegalStateException("Cannot access field on primitive or array type for " + target); } TypeDefinition current = parameters.get(0); do { FieldList<?> fields = current.getDeclaredFields().filter(not(isStatic()).<FieldDescription>and(isVisibleTo(instrumentedType)).and(matcher)); if (fields.size() == 1) { return fields.getOnly(); } else if (fields.size() > 1) { throw new IllegalStateException("Ambiguous field location of " + fields); } current = current.getSuperClass(); } while (current != null); throw new IllegalStateException("Cannot locate field matching " + matcher + " on " + targetType); } } }
A factory for a substitution that substitutes with a given field.
/** * A factory for a substitution that substitutes with a given field. */
@HashCodeAndEqualsPlugin.Enhance public static class OfGivenField implements Factory {
The field to substitute with.
/** * The field to substitute with. */
private final FieldDescription fieldDescription;
Creates a new factory that substitues with a given field.
Params:
  • fieldDescription – The field to substitute with.
/** * Creates a new factory that substitues with a given field. * * @param fieldDescription The field to substitute with. */
public OfGivenField(FieldDescription fieldDescription) { this.fieldDescription = fieldDescription; }
{@inheritDoc}
/** * {@inheritDoc} */
public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return new ForFieldAccess(instrumentedType, new FieldResolver.Simple(fieldDescription)); } }
A factory for a substitution that locates a field on the receiver type using a matcher.
/** * A factory for a substitution that locates a field on the receiver type using a matcher. */
@HashCodeAndEqualsPlugin.Enhance public static class OfMatchedField implements Factory {
The matcher to apply.
/** * The matcher to apply. */
private final ElementMatcher<? super FieldDescription> matcher;
Creates a new substitution factory that locates a field by applying a matcher on the receiver type.
Params:
  • matcher – The matcher to apply.
/** * Creates a new substitution factory that locates a field by applying a matcher on the receiver type. * * @param matcher The matcher to apply. */
public OfMatchedField(ElementMatcher<? super FieldDescription> matcher) { this.matcher = matcher; }
{@inheritDoc}
/** * {@inheritDoc} */
public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return new ForFieldAccess(instrumentedType, new FieldResolver.ForElementMatcher(instrumentedType, matcher)); } } }
A substitution with a method invocation.
/** * A substitution with a method invocation. */
@HashCodeAndEqualsPlugin.Enhance class ForMethodInvocation implements Substitution {
The index of the this reference within a non-static method.
/** * The index of the this reference within a non-static method. */
private static final int THIS_REFERENCE = 0;
The instrumented type.
/** * The instrumented type. */
private final TypeDescription instrumentedType;
The method resolver to use.
/** * The method resolver to use. */
private final MethodResolver methodResolver;
Creates a new method-resolving substitution.
Params:
  • instrumentedType – The instrumented type.
  • methodResolver – The method resolver to use.
/** * Creates a new method-resolving substitution. * * @param instrumentedType The instrumented type. * @param methodResolver The method resolver to use. */
public ForMethodInvocation(TypeDescription instrumentedType, MethodResolver methodResolver) { this.instrumentedType = instrumentedType; this.methodResolver = methodResolver; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { MethodDescription methodDescription = methodResolver.resolve(targetType, target, parameters, result); if (!methodDescription.isAccessibleTo(instrumentedType)) { throw new IllegalStateException(instrumentedType + " cannot access " + methodDescription); } TypeList.Generic mapped = methodDescription.isStatic() ? methodDescription.getParameters().asTypeList() : new TypeList.Generic.Explicit(CompoundList.of(methodDescription.getDeclaringType(), methodDescription.getParameters().asTypeList())); if (!methodDescription.getReturnType().asErasure().isAssignableTo(result.asErasure())) { throw new IllegalStateException("Cannot assign return value of " + methodDescription + " to " + result); } else if (mapped.size() != parameters.size()) { throw new IllegalStateException("Cannot invoke " + methodDescription + " on " + parameters); } for (int index = 0; index < mapped.size(); index++) { if (!mapped.get(index).asErasure().isAssignableTo(parameters.get(index).asErasure())) { throw new IllegalStateException("Cannot invoke " + methodDescription + " on " + parameters); } } return methodDescription.isVirtual() ? MethodInvocation.invoke(methodDescription).virtual(mapped.get(THIS_REFERENCE).asErasure()) : MethodInvocation.invoke(methodDescription); }
A method resolver for locating a method for a substitute.
/** * A method resolver for locating a method for a substitute. */
public interface MethodResolver {
Resolves the method to substitute with.
Params:
  • targetType – The target type on which a member is accessed.
  • target – The target field, method or constructor that is substituted,
  • parameters – All parameters that serve as input to this access.
  • result – The result that is expected from the interaction or void if no result is expected.
Returns:The field to substitute with.
/** * Resolves the method to substitute with. * * @param targetType The target type on which a member is accessed. * @param target The target field, method or constructor that is substituted, * @param parameters All parameters that serve as input to this access. * @param result The result that is expected from the interaction or {@code void} if no result is expected. * @return The field to substitute with. */
MethodDescription resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result);
A simple method resolver that returns a given method.
/** * A simple method resolver that returns a given method. */
@HashCodeAndEqualsPlugin.Enhance class Simple implements MethodResolver {
The method to substitute with.
/** * The method to substitute with. */
private final MethodDescription methodDescription;
Creates a new simple method resolver.
Params:
  • methodDescription – The method to substitute with.
/** * Creates a new simple method resolver. * * @param methodDescription The method to substitute with. */
public Simple(MethodDescription methodDescription) { this.methodDescription = methodDescription; }
{@inheritDoc}
/** * {@inheritDoc} */
public MethodDescription resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { return methodDescription; } }
A method resolver that locates a non-static method by locating it from the receiver type.
/** * A method resolver that locates a non-static method by locating it from the receiver type. */
@HashCodeAndEqualsPlugin.Enhance class Matching implements MethodResolver {
The instrumented type.
/** * The instrumented type. */
private final TypeDescription instrumentedType;
The method graph compiler to use.
/** * The method graph compiler to use. */
private final MethodGraph.Compiler methodGraphCompiler;
The matcher to use for locating the method to substitute with.
/** * The matcher to use for locating the method to substitute with. */
private final ElementMatcher<? super MethodDescription> matcher;
Creates a new matching method resolver.
Params:
  • instrumentedType – The instrumented type.
  • methodGraphCompiler – The method graph compiler to use.
  • matcher – The matcher to use for locating the method to substitute with.
/** * Creates a new matching method resolver. * * @param instrumentedType The instrumented type. * @param methodGraphCompiler The method graph compiler to use. * @param matcher The matcher to use for locating the method to substitute with. */
public Matching(TypeDescription instrumentedType, MethodGraph.Compiler methodGraphCompiler, ElementMatcher<? super MethodDescription> matcher) { this.instrumentedType = instrumentedType; this.methodGraphCompiler = methodGraphCompiler; this.matcher = matcher; }
{@inheritDoc}
/** * {@inheritDoc} */
public MethodDescription resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { if (parameters.isEmpty()) { throw new IllegalStateException("Cannot substitute parameterless instruction with " + target); } else if (parameters.get(0).isPrimitive() || parameters.get(0).isArray()) { throw new IllegalStateException("Cannot invoke method on primitive or array type for " + target); } TypeDefinition typeDefinition = parameters.get(0); List<MethodDescription> candidates = CompoundList.<MethodDescription>of(methodGraphCompiler.compile(typeDefinition, instrumentedType) .listNodes() .asMethodList() .filter(matcher), typeDefinition.getDeclaredMethods().filter(isPrivate().<MethodDescription>and(isVisibleTo(instrumentedType)).and(matcher))); if (candidates.size() == 1) { return candidates.get(0); } else { throw new IllegalStateException("Not exactly one method that matches " + matcher + ": " + candidates); } } } }
A factory for a substitution that invokes the instrumented method.
/** * A factory for a substitution that invokes the instrumented method. */
enum OfInstrumentedMethod implements Factory {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return new ForMethodInvocation(instrumentedType, new MethodResolver.Simple(instrumentedMethod)); } }
A factory for a substitution that invokes a given method.
/** * A factory for a substitution that invokes a given method. */
public static class OfGivenMethod implements Factory {
The method to invoke.
/** * The method to invoke. */
private final MethodDescription methodDescription;
Creates a new factory for a substitution that invokes a given method.
Params:
  • methodDescription – The method to invoke.
/** * Creates a new factory for a substitution that invokes a given method. * * @param methodDescription The method to invoke. */
public OfGivenMethod(MethodDescription methodDescription) { this.methodDescription = methodDescription; }
{@inheritDoc}
/** * {@inheritDoc} */
public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return new ForMethodInvocation(instrumentedType, new MethodResolver.Simple(methodDescription)); } }
A factory for a substitution that locates a method on the receiver type using a matcher.
/** * A factory for a substitution that locates a method on the receiver type using a matcher. */
public static class OfMatchedMethod implements Factory {
The matcher for locating the method to substitute with.
/** * The matcher for locating the method to substitute with. */
private final ElementMatcher<? super MethodDescription> matcher;
The method graph compiler to use.
/** * The method graph compiler to use. */
private final MethodGraph.Compiler methodGraphCompiler;
Creates a factory for a substitution that locates a method on the receiver type.
Params:
  • matcher – The matcher for locating the method to substitute with.
  • methodGraphCompiler – The method graph compiler to use.
/** * Creates a factory for a substitution that locates a method on the receiver type. * * @param matcher The matcher for locating the method to substitute with. * @param methodGraphCompiler The method graph compiler to use. */
public OfMatchedMethod(ElementMatcher<? super MethodDescription> matcher, MethodGraph.Compiler methodGraphCompiler) { this.matcher = matcher; this.methodGraphCompiler = methodGraphCompiler; }
{@inheritDoc}
/** * {@inheritDoc} */
public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return new ForMethodInvocation(instrumentedType, new MethodResolver.Matching(instrumentedType, methodGraphCompiler, matcher)); } } } }
A replacement combines a Substitution and a way of choosing if this substitution should be applied for a discovered member.
/** * A replacement combines a {@link Substitution} and a way of choosing if this substitution should be applied for a discovered member. */
protected interface Replacement {
Binds this replacement for a field that was discovered.
Params:
  • fieldDescription – The field that was discovered.
  • writeAccess – true if this field was written to.
Returns:A binding for the discovered field access.
/** * Binds this replacement for a field that was discovered. * * @param fieldDescription The field that was discovered. * @param writeAccess {@code true} if this field was written to. * @return A binding for the discovered field access. */
Binding bind(FieldDescription.InDefinedShape fieldDescription, boolean writeAccess);
Binds this replacement for a field that was discovered.
Params:
  • typeDescription – The type on which the method was invoked.
  • methodDescription – The method that was discovered.
  • invocationType – The invocation type for this method.
Returns:A binding for the discovered method invocation.
/** * Binds this replacement for a field that was discovered. * * @param typeDescription The type on which the method was invoked. * @param methodDescription The method that was discovered. * @param invocationType The invocation type for this method. * @return A binding for the discovered method invocation. */
Binding bind(TypeDescription typeDescription, MethodDescription methodDescription, InvocationType invocationType);
A binding for a replacement of a field or method access within another method.
/** * A binding for a replacement of a field or method access within another method. */
interface Binding {
Returns true if this binding is resolved.
Returns:true if this binding is resolved.
/** * Returns {@code true} if this binding is resolved. * * @return {@code true} if this binding is resolved. */
boolean isBound();
Creates a stack manipulation that represents the substitution. This method can only be called for actually bound bindings.
Params:
  • parameters – The parameters that are accessible to the substitution target.
  • result – The result that is expected from the substitution target or void if none is expected.
Returns:A stack manipulation that represents the replacement.
/** * Creates a stack manipulation that represents the substitution. This method can only be called for actually bound bindings. * * @param parameters The parameters that are accessible to the substitution target. * @param result The result that is expected from the substitution target or {@code void} if none is expected. * @return A stack manipulation that represents the replacement. */
StackManipulation make(TypeList.Generic parameters, TypeDescription.Generic result);
An unresolved binding.
/** * An unresolved binding. */
enum Unresolved implements Binding {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isBound() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation make(TypeList.Generic parameters, TypeDescription.Generic result) { throw new IllegalStateException(); } }
A binding that was resolved for an actual substitution.
/** * A binding that was resolved for an actual substitution. */
@HashCodeAndEqualsPlugin.Enhance class Resolved implements Binding {
The type on which a field or method was accessed.
/** * The type on which a field or method was accessed. */
private final TypeDescription targetType;
The field or method that was accessed.
/** * The field or method that was accessed. */
private final ByteCodeElement target;
The substitution to apply.
/** * The substitution to apply. */
private final Substitution substitution;
Creates a new resolved binding.
Params:
  • targetType – The type on which a field or method was accessed.
  • target – The field or method that was accessed.
  • substitution – The substitution to apply.
/** * Creates a new resolved binding. * * @param targetType The type on which a field or method was accessed. * @param target The field or method that was accessed. * @param substitution The substitution to apply. */
protected Resolved(TypeDescription targetType, ByteCodeElement target, Substitution substitution) { this.targetType = targetType; this.target = target; this.substitution = substitution; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isBound() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation make(TypeList.Generic parameters, TypeDescription.Generic result) { return substitution.resolve(targetType, target, parameters, result); } } }
A factory for creating a replacement for an instrumented method.
/** * A factory for creating a replacement for an instrumented method. */
interface Factory {
Creates a replacement for an instrumented method.
Params:
  • instrumentedType – The instrumented type.
  • instrumentedMethod – The instrumented method.
  • typePool – The type pool being used within the member substitution being applied.
Returns:A replacement to use within the supplied instrumented method.
/** * Creates a replacement for an instrumented method. * * @param instrumentedType The instrumented type. * @param instrumentedMethod The instrumented method. * @param typePool The type pool being used within the member substitution being applied. * @return A replacement to use within the supplied instrumented method. */
Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool);
A compound factory.
/** * A compound factory. */
@HashCodeAndEqualsPlugin.Enhance class Compound implements Factory {
A list of represented factories.
/** * A list of represented factories. */
private final List<Factory> factories;
Creates a new compound factory.
Params:
  • factory – A list of represented factories.
/** * Creates a new compound factory. * * @param factory A list of represented factories. */
protected Compound(Factory... factory) { this(Arrays.asList(factory)); }
Creates a new compound factory.
Params:
  • factories – A list of represented factories.
/** * Creates a new compound factory. * * @param factories A list of represented factories. */
protected Compound(List<? extends Factory> factories) { this.factories = new ArrayList<Factory>(); for (Factory factory : factories) { if (factory instanceof Compound) { this.factories.addAll(((Compound) factory).factories); } else if (!(factory instanceof NoOp)) { this.factories.add(factory); } } }
{@inheritDoc}
/** * {@inheritDoc} */
public Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { List<Replacement> replacements = new ArrayList<Replacement>(); for (Factory factory : factories) { replacements.add(factory.make(instrumentedType, instrumentedMethod, typePool)); } return new ForFirstBinding(replacements); } } }
Describes a method invocation type.
/** * Describes a method invocation type. */
enum InvocationType {
Desribes a virtual method invocation.
/** * Desribes a virtual method invocation. */
VIRTUAL,
Describes a super method invocation.
/** * Describes a super method invocation. */
SUPER,
Describes any method invocation that is not virtual or a super method invocation.
/** * Describes any method invocation that is not virtual or a super method invocation. */
OTHER;
Resolves an invocation type.
Params:
  • opcode – The opcode that is used for invoking the method.
  • methodDescription – The method that is being invoked.
Returns:The invokation type for the method given that opcode.
/** * Resolves an invocation type. * * @param opcode The opcode that is used for invoking the method. * @param methodDescription The method that is being invoked. * @return The invokation type for the method given that opcode. */
protected static InvocationType of(int opcode, MethodDescription methodDescription) { switch (opcode) { case Opcodes.INVOKEVIRTUAL: case Opcodes.INVOKEINTERFACE: return InvocationType.VIRTUAL; case Opcodes.INVOKESPECIAL: return methodDescription.isVirtual() ? SUPER : OTHER; default: return OTHER; } }
Checks if this invokation type matches the specified inputs.
Params:
  • includeVirtualCalls – true if a virtual method should be matched.
  • includeSuperCalls – true if a super method call should be matched.
Returns:true if this invocation type matches the specified parameters.
/** * Checks if this invokation type matches the specified inputs. * * @param includeVirtualCalls {@code true} if a virtual method should be matched. * @param includeSuperCalls {@code true} if a super method call should be matched. * @return {@code true} if this invocation type matches the specified parameters. */
protected boolean matches(boolean includeVirtualCalls, boolean includeSuperCalls) { switch (this) { case VIRTUAL: return includeVirtualCalls; case SUPER: return includeSuperCalls; default: return true; } } }
A non-operational replacement.
/** * A non-operational replacement. */
enum NoOp implements Replacement, Factory {
The singelton instance.
/** * The singelton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public Binding bind(FieldDescription.InDefinedShape fieldDescription, boolean writeAccess) { return Binding.Unresolved.INSTANCE; }
{@inheritDoc}
/** * {@inheritDoc} */
public Binding bind(TypeDescription typeDescription, MethodDescription methodDescription, InvocationType invocationType) { return Binding.Unresolved.INSTANCE; } }
A replacement that substitutes a member based on a row of element matchers.
/** * A replacement that substitutes a member based on a row of element matchers. */
@HashCodeAndEqualsPlugin.Enhance class ForElementMatchers implements Replacement {
The field matcher to consider when discovering fields.
/** * The field matcher to consider when discovering fields. */
private final ElementMatcher<? super FieldDescription.InDefinedShape> fieldMatcher;
The method matcher to consider when discovering methods.
/** * The method matcher to consider when discovering methods. */
private final ElementMatcher<? super MethodDescription> methodMatcher;
true if field reading access should be matched.
/** * {@code true} if field reading access should be matched. */
private final boolean matchFieldRead;
true if field writing access should be matched.
/** * {@code true} if field writing access should be matched. */
private final boolean matchFieldWrite;
true if virtual method calls should be matched.
/** * {@code true} if virtual method calls should be matched. */
private final boolean includeVirtualCalls;
true if super method calls should be matched.
/** * {@code true} if super method calls should be matched. */
private final boolean includeSuperCalls;
The substitution to trigger if a member is matched.
/** * The substitution to trigger if a member is matched. */
private final Substitution substitution;
Creates a new replacement that triggers a substitution based on a row of matchers.
Params:
  • fieldMatcher – The field matcher to consider when discovering fields.
  • methodMatcher – The method matcher to consider when discovering methods.
  • matchFieldRead – true if field reading access should be matched.
  • matchFieldWrite – true if field writing access should be matched.
  • includeVirtualCalls – true if virtual method calls should be matched.
  • includeSuperCalls – true if super method calls should be matched.
  • substitution – The substitution to trigger if a member is matched.
/** * Creates a new replacement that triggers a substitution based on a row of matchers. * * @param fieldMatcher The field matcher to consider when discovering fields. * @param methodMatcher The method matcher to consider when discovering methods. * @param matchFieldRead {@code true} if field reading access should be matched. * @param matchFieldWrite {@code true} if field writing access should be matched. * @param includeVirtualCalls {@code true} if virtual method calls should be matched. * @param includeSuperCalls {@code true} if super method calls should be matched. * @param substitution The substitution to trigger if a member is matched. */
protected ForElementMatchers(ElementMatcher<? super FieldDescription.InDefinedShape> fieldMatcher, ElementMatcher<? super MethodDescription> methodMatcher, boolean matchFieldRead, boolean matchFieldWrite, boolean includeVirtualCalls, boolean includeSuperCalls, Substitution substitution) { this.fieldMatcher = fieldMatcher; this.methodMatcher = methodMatcher; this.matchFieldRead = matchFieldRead; this.matchFieldWrite = matchFieldWrite; this.includeVirtualCalls = includeVirtualCalls; this.includeSuperCalls = includeSuperCalls; this.substitution = substitution; }
{@inheritDoc}
/** * {@inheritDoc} */
public Binding bind(FieldDescription.InDefinedShape fieldDescription, boolean writeAccess) { return (writeAccess ? matchFieldWrite : matchFieldRead) && fieldMatcher.matches(fieldDescription) ? new Binding.Resolved(fieldDescription.getDeclaringType(), fieldDescription, substitution) : Binding.Unresolved.INSTANCE; }
{@inheritDoc}
/** * {@inheritDoc} */
public Binding bind(TypeDescription typeDescription, MethodDescription methodDescription, InvocationType invocationType) { return invocationType.matches(includeVirtualCalls, includeSuperCalls) && methodMatcher.matches(methodDescription) ? new Binding.Resolved(typeDescription, methodDescription, substitution) : Binding.Unresolved.INSTANCE; }
A factory for creating a replacement that chooses members based on a row of element matchers.
/** * A factory for creating a replacement that chooses members based on a row of element matchers. */
@HashCodeAndEqualsPlugin.Enhance protected static class Factory implements Replacement.Factory {
The field matcher to consider when discovering fields.
/** * The field matcher to consider when discovering fields. */
private final ElementMatcher<? super FieldDescription.InDefinedShape> fieldMatcher;
The method matcher to consider when discovering methods.
/** * The method matcher to consider when discovering methods. */
private final ElementMatcher<? super MethodDescription> methodMatcher;
true if field reading access should be matched.
/** * {@code true} if field reading access should be matched. */
private final boolean matchFieldRead;
true if field writing access should be matched.
/** * {@code true} if field writing access should be matched. */
private final boolean matchFieldWrite;
true if virtual method calls should be matched.
/** * {@code true} if virtual method calls should be matched. */
private final boolean includeVirtualCalls;
true if super method calls should be matched.
/** * {@code true} if super method calls should be matched. */
private final boolean includeSuperCalls;
The substitution factory to create a substitution from.
/** * The substitution factory to create a substitution from. */
private final Substitution.Factory substitutionFactory;
Creates a new replacement that triggers a substitution based on a row of matchers.
Params:
  • fieldMatcher – The field matcher to consider when discovering fields.
  • methodMatcher – The method matcher to consider when discovering methods.
  • matchFieldRead – true if field reading access should be matched.
  • matchFieldWrite – true if field writing access should be matched.
  • includeVirtualCalls – true if virtual method calls should be matched.
  • includeSuperCalls – true if super method calls should be matched.
  • substitutionFactory – The substitution factory to create a substitution from.
/** * Creates a new replacement that triggers a substitution based on a row of matchers. * * @param fieldMatcher The field matcher to consider when discovering fields. * @param methodMatcher The method matcher to consider when discovering methods. * @param matchFieldRead {@code true} if field reading access should be matched. * @param matchFieldWrite {@code true} if field writing access should be matched. * @param includeVirtualCalls {@code true} if virtual method calls should be matched. * @param includeSuperCalls {@code true} if super method calls should be matched. * @param substitutionFactory The substitution factory to create a substitution from. */
protected Factory(ElementMatcher<? super FieldDescription.InDefinedShape> fieldMatcher, ElementMatcher<? super MethodDescription> methodMatcher, boolean matchFieldRead, boolean matchFieldWrite, boolean includeVirtualCalls, boolean includeSuperCalls, Substitution.Factory substitutionFactory) { this.fieldMatcher = fieldMatcher; this.methodMatcher = methodMatcher; this.matchFieldRead = matchFieldRead; this.matchFieldWrite = matchFieldWrite; this.includeVirtualCalls = includeVirtualCalls; this.includeSuperCalls = includeSuperCalls; this.substitutionFactory = substitutionFactory; }
Creates a factory for applying a substitution on all matched byte code elements for all access types.
Params:
  • matcher – The matcher to apply.
  • factory – The substitution factory to create a substitution from.
Returns:An appropriate replacement factory for the supplied matcher and substitution factory.
/** * Creates a factory for applying a substitution on all matched byte code elements for all access types. * * @param matcher The matcher to apply. * @param factory The substitution factory to create a substitution from. * @return An appropriate replacement factory for the supplied matcher and substitution factory. */
protected static Replacement.Factory of(ElementMatcher<? super ByteCodeElement> matcher, Substitution.Factory factory) { return new Factory(matcher, matcher, true, true, true, true, factory); }
Creates a factory that only matches field access for given access types.
Params:
  • matcher – The matcher to identify fields for substitution.
  • matchFieldRead – true if field read access should be matched.
  • matchFieldWrite – true if field write access should be matched.
  • factory – The substitution factory to apply for fields that match the specified criteria.
Returns:An appropriate replacement factory.
/** * Creates a factory that only matches field access for given access types. * * @param matcher The matcher to identify fields for substitution. * @param matchFieldRead {@code true} if field read access should be matched. * @param matchFieldWrite {@code true} if field write access should be matched. * @param factory The substitution factory to apply for fields that match the specified criteria. * @return An appropriate replacement factory. */
protected static Replacement.Factory ofField(ElementMatcher<? super FieldDescription.InDefinedShape> matcher, boolean matchFieldRead, boolean matchFieldWrite, Substitution.Factory factory) { return new Factory(matcher, none(), matchFieldRead, matchFieldWrite, false, false, factory); }
Creates a factory that only matches method and constructor invocations for given invocation types.
Params:
  • matcher – The matcher to identify methods and constructors for substitution.
  • includeVirtualCalls – true if virtual method calls should be matched.
  • includeSuperCalls – true if super method calls should be matched.
  • factory – The substitution factory to apply for methods and constructors that match the specified criteria.
Returns:An appropriate replacement factory.
/** * Creates a factory that only matches method and constructor invocations for given invocation types. * * @param matcher The matcher to identify methods and constructors for substitution. * @param includeVirtualCalls {@code true} if virtual method calls should be matched. * @param includeSuperCalls {@code true} if super method calls should be matched. * @param factory The substitution factory to apply for methods and constructors that match the specified criteria. * @return An appropriate replacement factory. */
protected static Replacement.Factory ofMethod(ElementMatcher<? super MethodDescription> matcher, boolean includeVirtualCalls, boolean includeSuperCalls, Substitution.Factory factory) { return new Factory(none(), matcher, false, false, includeVirtualCalls, includeSuperCalls, factory); }
{@inheritDoc}
/** * {@inheritDoc} */
public Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) { return new ForElementMatchers(fieldMatcher, methodMatcher, matchFieldRead, matchFieldWrite, includeVirtualCalls, includeSuperCalls, substitutionFactory.make(instrumentedType, instrumentedMethod, typePool)); } } }
A replacement that only resolves the first matching replacement of a list of replacements.
/** * A replacement that only resolves the first matching replacement of a list of replacements. */
@HashCodeAndEqualsPlugin.Enhance class ForFirstBinding implements Replacement {
The list of replacements to consider.
/** * The list of replacements to consider. */
private final List<? extends Replacement> replacements;
Creates a new replacement that triggers the first matching replacement, if any.
Params:
  • replacements – The list of replacements to consider.
/** * Creates a new replacement that triggers the first matching replacement, if any. * * @param replacements The list of replacements to consider. */
protected ForFirstBinding(List<? extends Replacement> replacements) { this.replacements = replacements; }
{@inheritDoc}
/** * {@inheritDoc} */
public Binding bind(FieldDescription.InDefinedShape fieldDescription, boolean writeAccess) { for (Replacement replacement : replacements) { Binding binding = replacement.bind(fieldDescription, writeAccess); if (binding.isBound()) { return binding; } } return Binding.Unresolved.INSTANCE; }
{@inheritDoc}
/** * {@inheritDoc} */
public Binding bind(TypeDescription typeDescription, MethodDescription methodDescription, InvocationType invocationType) { for (Replacement replacement : replacements) { Binding binding = replacement.bind(typeDescription, methodDescription, invocationType); if (binding.isBound()) { return binding; } } return Binding.Unresolved.INSTANCE; } } }
A method visitor that applies a substitution for matched methods.
/** * A method visitor that applies a substitution for matched methods. */
protected static class SubstitutingMethodVisitor extends MethodVisitor {
The instrumented type.
/** * The instrumented type. */
private final TypeDescription instrumentedType;
The method graph compiler to use.
/** * The method graph compiler to use. */
private final MethodGraph.Compiler methodGraphCompiler;
true if the method processing should be strict where an exception is raised if a member cannot be found.
/** * {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. */
private final boolean strict;
The replacement to use for creating substitutions.
/** * The replacement to use for creating substitutions. */
private final Replacement replacement;
The implementation context to use.
/** * The implementation context to use. */
private final Implementation.Context implementationContext;
The type pool to use.
/** * The type pool to use. */
private final TypePool typePool;
An additional buffer for the operand stack that is required.
/** * An additional buffer for the operand stack that is required. */
private int stackSizeBuffer;
Creates a new substituting method visitor.
Params:
  • methodVisitor – The method visitor to delegate to.
  • instrumentedType – The instrumented type.
  • methodGraphCompiler – The method graph compiler to use.
  • strict – true if the method processing should be strict where an exception is raised if a member cannot be found.
  • replacement – The replacement to use for creating substitutions.
  • implementationContext – The implementation context to use.
  • typePool – The type pool to use.
/** * Creates a new substituting method visitor. * * @param methodVisitor The method visitor to delegate to. * @param instrumentedType The instrumented type. * @param methodGraphCompiler The method graph compiler to use. * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found. * @param replacement The replacement to use for creating substitutions. * @param implementationContext The implementation context to use. * @param typePool The type pool to use. */
protected SubstitutingMethodVisitor(MethodVisitor methodVisitor, TypeDescription instrumentedType, MethodGraph.Compiler methodGraphCompiler, boolean strict, Replacement replacement, Implementation.Context implementationContext, TypePool typePool) { super(OpenedClassReader.ASM_API, methodVisitor); this.instrumentedType = instrumentedType; this.methodGraphCompiler = methodGraphCompiler; this.strict = strict; this.replacement = replacement; this.implementationContext = implementationContext; this.typePool = typePool; stackSizeBuffer = 0; } @Override public void visitFieldInsn(int opcode, String owner, String internalName, String descriptor) { TypePool.Resolution resolution = typePool.describe(owner.replace('/', '.')); if (resolution.isResolved()) { FieldList<FieldDescription.InDefinedShape> candidates = resolution.resolve().getDeclaredFields().filter(strict ? ElementMatchers.<FieldDescription>named(internalName).and(hasDescriptor(descriptor)) : ElementMatchers.<FieldDescription>failSafe(named(internalName).and(hasDescriptor(descriptor)))); if (!candidates.isEmpty()) { Replacement.Binding binding = replacement.bind(candidates.getOnly(), opcode == Opcodes.PUTFIELD || opcode == Opcodes.PUTSTATIC); if (binding.isBound()) { TypeList.Generic parameters; TypeDescription.Generic result; switch (opcode) { case Opcodes.PUTFIELD: parameters = new TypeList.Generic.Explicit(candidates.getOnly().getDeclaringType(), candidates.getOnly().getType()); result = TypeDescription.Generic.VOID; break; case Opcodes.PUTSTATIC: parameters = new TypeList.Generic.Explicit(candidates.getOnly().getType()); result = TypeDescription.Generic.VOID; break; case Opcodes.GETFIELD: parameters = new TypeList.Generic.Explicit(candidates.getOnly().getDeclaringType()); result = candidates.getOnly().getType(); break; case Opcodes.GETSTATIC: parameters = new TypeList.Generic.Empty(); result = candidates.getOnly().getType(); break; default: throw new AssertionError(); } stackSizeBuffer = Math.max(stackSizeBuffer, binding.make(parameters, result) .apply(mv, implementationContext) .getMaximalSize() - result.getStackSize().getSize()); return; } } else if (strict) { throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + "." + internalName + descriptor + " using " + typePool); } } else if (strict) { throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + " using " + typePool); } super.visitFieldInsn(opcode, owner, internalName, descriptor); } @Override public void visitMethodInsn(int opcode, String owner, String internalName, String descriptor, boolean isInterface) { TypePool.Resolution resolution = typePool.describe(owner.replace('/', '.')); if (resolution.isResolved()) { MethodList<?> candidates; if (opcode == Opcodes.INVOKESPECIAL && internalName.equals(MethodDescription.CONSTRUCTOR_INTERNAL_NAME)) { candidates = resolution.resolve().getDeclaredMethods().filter(strict ? ElementMatchers.<MethodDescription>isConstructor().and(hasDescriptor(descriptor)) : ElementMatchers.<MethodDescription>failSafe(isConstructor().and(hasDescriptor(descriptor)))); } else if (opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL) { candidates = resolution.resolve().getDeclaredMethods().filter(strict ? ElementMatchers.<MethodDescription>named(internalName).and(hasDescriptor(descriptor)) : ElementMatchers.<MethodDescription>failSafe(named(internalName).and(hasDescriptor(descriptor)))); } else { // Invokevirtual and invokeinterface can represent a private, non-static method from Java 11. candidates = resolution.resolve().getDeclaredMethods().filter(strict ? ElementMatchers.<MethodDescription>isPrivate().and(not(isStatic())).and(named(internalName).and(hasDescriptor(descriptor))) : ElementMatchers.<MethodDescription>failSafe(isPrivate().<MethodDescription>and(not(isStatic())).and(named(internalName).and(hasDescriptor(descriptor))))); if (candidates.isEmpty()) { candidates = methodGraphCompiler.compile(resolution.resolve(), instrumentedType).listNodes().asMethodList().filter(strict ? ElementMatchers.<MethodDescription>named(internalName).and(hasDescriptor(descriptor)) : ElementMatchers.<MethodDescription>failSafe(named(internalName).and(hasDescriptor(descriptor)))); } } if (!candidates.isEmpty()) { Replacement.Binding binding = replacement.bind(resolution.resolve(), candidates.getOnly(), Replacement.InvocationType.of(opcode, candidates.getOnly())); if (binding.isBound()) { stackSizeBuffer = Math.max(stackSizeBuffer, binding.make( candidates.getOnly().isStatic() || candidates.getOnly().isConstructor() ? candidates.getOnly().getParameters().asTypeList() : new TypeList.Generic.Explicit(CompoundList.of(resolution.resolve(), candidates.getOnly().getParameters().asTypeList())), candidates.getOnly().isConstructor() ? candidates.getOnly().getDeclaringType().asGenericType() : candidates.getOnly().getReturnType()) .apply(mv, implementationContext).getMaximalSize() - (candidates.getOnly().isConstructor() ? StackSize.SINGLE : candidates.getOnly().getReturnType().getStackSize()).getSize()); if (candidates.getOnly().isConstructor()) { stackSizeBuffer = Math.max(stackSizeBuffer, new StackManipulation.Compound( Duplication.SINGLE.flipOver(TypeDescription.OBJECT), Removal.SINGLE, Removal.SINGLE, Duplication.SINGLE.flipOver(TypeDescription.OBJECT), Removal.SINGLE, Removal.SINGLE ).apply(mv, implementationContext).getMaximalSize() + StackSize.SINGLE.getSize()); } return; } } else if (strict) { throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + "." + internalName + descriptor + " using " + typePool); } } else if (strict) { throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + " using " + typePool); } super.visitMethodInsn(opcode, owner, internalName, descriptor, isInterface); } @Override public void visitMaxs(int maxStack, int maxLocals) { super.visitMaxs(maxStack + stackSizeBuffer, maxLocals); } } }