All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.bytebuddy.implementation.MethodDelegation Maven / Gradle / Ivy

Go to download

Byte Buddy is a Java library for creating Java classes at run time. This artifact is a build of Byte Buddy with all ASM dependencies repackaged into its own name space.

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

import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.scaffold.FieldLocator;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.implementation.bind.MethodDelegationBinder;
import net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.Duplication;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.TypeCreation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.utility.CompoundList;
import net.bytebuddy.utility.RandomString;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

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

/**
 * This implementation delegates a method call to another method which can either be {@code static} by providing
 * a reference to a {@link java.lang.Class} or an instance method when another object is provided. The potential
 * targets of the method delegation can further be filtered by applying a filter. The method delegation can be
 * customized by invoking the {@code MethodDelegation}'s several builder methods.
 * 

 

* Without any customization, the method delegation will work as follows: *

 

* Binding an instrumented method to a given delegate method *

 

* A method will be bound parameter by parameter. Considering a method {@code Foo#bar} being bound to a method * {@code Qux#baz}, the method delegation will be decided on basis of the following annotations: *
    *
  • {@link net.bytebuddy.implementation.bind.annotation.Argument}: * This annotation will bind the {@code n}-th parameter of {@code Foo#bar} to that parameter of {@code Qux#baz}that * is annotated with this annotation where {@code n} is the obligatory argument of the {@code @Argument} annotation.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.AllArguments}: * This annotation will assign a collection of all parameters of {@code Foo#bar} to that parameter of {@code Qux#baz} * that is annotated with {@code AllArguments}.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.This}: A parameter * of {@code Qux#baz} that is annotated with {@code This} will be assigned the instance that is instrumented for * a non-static method.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.Super}: A parameter that is annotated with * this annotation is assigned a proxy that allows calling an instrumented type's super methods.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.Default}: A parameter that is annotated with * this annotation is assigned a proxy that allows calling an instrumented type's directly implemented interfaces' * default methods.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.SuperCall}: A parameter * of {@code Qux#baz} that is annotated with {@code SuperCall} will be assigned an instance of a type implementing both * {@link java.lang.Runnable} and {@link java.util.concurrent.Callable} which will invoke the instrumented method on the * invocation of either interface's method. The call is made using the original arguments of the method invocation. * The return value is only emitted for the {@link java.util.concurrent.Callable#call()} method which additionally * requires to catch any unchecked exceptions that might be thrown by the original method's implementation. If a * source method is abstract, using this annotation excludes the method with this parameter annotation from being bound * to this source method. *
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.DefaultCall}: * This annotation is similar to the {@link net.bytebuddy.implementation.bind.annotation.SuperCall} * annotation but it invokes a default method that is compatible to this method. If a source method does not represent * a default method, using this annotation excludes the method with this parameter annotation from being bound to this * source method. For using method handles, the {@link net.bytebuddy.implementation.bind.annotation.SuperCallHandle} * and {@link net.bytebuddy.implementation.bind.annotation.DefaultCallHandle} annotations can be used.
  • *
  • The {@link net.bytebuddy.implementation.bind.annotation.SuperMethod} or * {@link net.bytebuddy.implementation.bind.annotation.DefaultMethod} annotations can be used on any parameter type * that is assignable from the {@link java.lang.reflect.Method} type. the parameter is bound a method instance that * allows for the reflective invocation of a super or default method. Note that this method is not equal to the intercepted * method but represents a synthetic accessor method. Using this annotation also causes this accessor to be {@code public} * which allows its outside invocation without any access checks by a security manager. For using method handles, the * {@link net.bytebuddy.implementation.bind.annotation.SuperCallHandle} and * {@link net.bytebuddy.implementation.bind.annotation.DefaultCallHandle} annotations can be used.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.Origin}: A parameter of * {@code Qux#baz} that is annotated with {@code Origin} is assigned a reference to either a {@link java.lang.reflect.Method}, * a {@link java.lang.reflect.Constructor}, a {@code java.lang.reflect.Executable} or a {@link java.lang.Class} instance. * A {@code Method}-typed, {@code Constructor} or {@code Executable} parameter is assigned a reference to the original * method that is instrumented. A {@code Class}-typed parameter is assigned the type of the caller. Furthermore, {@code MethodType} * and {@code MethodHandle} parameters are also supported. When using the annotation on a {@link java.lang.String} type, * the intercepted method's {@code toString} value is injected. The same holds for a parameter of type {@code int} that receives * the modifiers of the instrumented method.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.StubValue}: Assigns the (boxed) default value of the * intercepted method's return type to the parameter. If the return type is {@code void}, {@code null} is assigned.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.Empty}: Assigns the parameter type's * default value, i.e. {@code null} for a reference type or zero for primitive types. This is an opportunity to * ignore a parameter.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.Pipe}: A parameter that is annotated * with this annotation is assigned a proxy for forwarding the source method invocation to another instance of the * same type as the declaring type of the intercepted method. This annotation needs to be installed and explicitly * registered before it can be used. See the {@link net.bytebuddy.implementation.bind.annotation.Pipe} * annotation's documentation for further information on how this can be done.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.Morph}: The morph annotation is similar to * the {@link net.bytebuddy.implementation.bind.annotation.SuperCall} annotation but allows to * explicitly define and therewith alter the arguments that are handed to the super method. This annotation needs * to be installed and explicitly registered before it can be used. See the documentation to the annotation for * further information.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.FieldValue}: Allows to access a field's value at the time * of the method invocation. The field's value is directly assigned to the annotated parameter.
  • *
  • {@link net.bytebuddy.implementation.bind.annotation.FieldProxy}: Allows to access fields via getter * and setter proxies. This annotation needs to be installed and explicitly registered before it can be used. * Note that any field access requires boxing such that a use of {@link net.bytebuddy.implementation.FieldAccessor} in * combination with {@link net.bytebuddy.implementation.MethodDelegation#andThen(Implementation)} might be a more * performant alternative for implementing field getters and setters.
  • *
* If a method is not annotated with any of the above methods, it will be treated as if it was annotated * {@link net.bytebuddy.implementation.bind.annotation.Argument} using the next * unbound parameter index of the source method as its parameter. This means that a method * {@code Qux#baz(@Argument(2) Object p1, Object p2, @Argument(0) Object p3} would be treated as if {@code p2} was annotated * with {@code @Argument(1)}. *

 

* In addition, the {@link net.bytebuddy.implementation.bind.annotation.RuntimeType} * annotation can instruct a parameter to be bound by a * {@link net.bytebuddy.implementation.bytecode.assign.Assigner} with considering the * runtime type of the parameter. *

 

* Selecting among different methods that can be used for binding a method * of the instrumented type *

 

* When deciding between two methods {@code Foo#bar} and {@code Foo#qux} that could both be used to delegating a * method call, the following consideration is applied in the given order: *
    *
  1. {@link net.bytebuddy.implementation.bind.annotation.BindingPriority}: * A method that is annotated with this annotation is given a specific priority where the default priority is set * to {@link net.bytebuddy.implementation.bind.annotation.BindingPriority#DEFAULT} * for non-annotated method. A method with a higher priority is considered a better target for delegation.
  2. *
  3. {@link net.bytebuddy.implementation.bind.DeclaringTypeResolver}: * If a target method is declared by a more specific type than another method, the method with the most specific * type is bound.
  4. *
  5. {@link net.bytebuddy.implementation.bind.MethodNameEqualityResolver}: * If a source method {@code Baz#qux} is the source method, it will rather be assigned to {@code Foo#qux} because * of their equal names. Similar names and case-insensitive equality are not considered.
  6. *
  7. {@link net.bytebuddy.implementation.bind.ArgumentTypeResolver}: * The most specific type resolver will consider all bindings that are using the * {@link net.bytebuddy.implementation.bind.annotation.Argument} * annotation for resolving a binding conflict. In this context, the resolution will equal the most-specific * type resolution that is performed by the Java compiler. This means that a source method {@code Bar#baz(String)} * will rather be bound to a method {@code Foo#bar(String)} than {@code Foo#qux(Object)} because the {@code String} * type is more specific than the {@code Object} type. If two methods are equally adequate by their parameter types, * then the method with the higher numbers of {@code @Argument} annotated parameters is considered as the better * delegation target.
  8. *
  9. {@link net.bytebuddy.implementation.bind.ParameterLengthResolver}: * If a target methods has a higher number of total parameters that were successfully bound, the method with * the higher number will be considered as the better delegation target.
  10. *
*

* Additionally, if a method is annotated by * {@link net.bytebuddy.implementation.bind.annotation.IgnoreForBinding}, * it is never considered as a target for a method delegation. *

*

* Important: For invoking a method on another instance, use the {@link MethodCall} implementation. A method delegation * intends to bind a interceptor class and its resolution algorithm will not necessarily yield a delegation to the intercepted * method. *

* * @see MethodCall * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFixedValue */ @HashCodeAndEqualsPlugin.Enhance public class MethodDelegation implements Implementation.Composable { /** * The implementation delegate for this method delegation. */ private final ImplementationDelegate implementationDelegate; /** * A list of {@link net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder.ParameterBinder}s * to be used by this method delegation. */ private final List> parameterBinders; /** * The {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver} * to be used by this method delegation. */ private final MethodDelegationBinder.AmbiguityResolver ambiguityResolver; /** * The termination handler to apply. */ private final TargetMethodAnnotationDrivenBinder.TerminationHandler terminationHandler; /** * The binding resolver being used to select the relevant method binding. */ private final MethodDelegationBinder.BindingResolver bindingResolver; /** * The {@link net.bytebuddy.implementation.bytecode.assign.Assigner} to be used by this method delegation. */ private final Assigner assigner; /** * Creates a new method delegation. * * @param implementationDelegate The implementation delegate to use by this method delegator. * @param parameterBinders The parameter binders to use by this method delegator. * @param ambiguityResolver The ambiguity resolver to use by this method delegator. * @param bindingResolver The binding resolver being used to select the relevant method binding. */ protected MethodDelegation(ImplementationDelegate implementationDelegate, List> parameterBinders, MethodDelegationBinder.AmbiguityResolver ambiguityResolver, MethodDelegationBinder.BindingResolver bindingResolver) { this(implementationDelegate, parameterBinders, ambiguityResolver, MethodDelegationBinder.TerminationHandler.Default.RETURNING, bindingResolver, Assigner.DEFAULT); } /** * Creates a new method delegation. * * @param implementationDelegate The implementation delegate to use by this method delegator. * @param parameterBinders The parameter binders to use by this method delegator. * @param ambiguityResolver The ambiguity resolver to use by this method delegator. * @param terminationHandler The termination handler to apply. * @param bindingResolver The binding resolver being used to select the relevant method binding. * @param assigner The assigner to be supplied by this method delegator. */ private MethodDelegation(ImplementationDelegate implementationDelegate, List> parameterBinders, MethodDelegationBinder.AmbiguityResolver ambiguityResolver, TargetMethodAnnotationDrivenBinder.TerminationHandler terminationHandler, MethodDelegationBinder.BindingResolver bindingResolver, Assigner assigner) { this.implementationDelegate = implementationDelegate; this.parameterBinders = parameterBinders; this.terminationHandler = terminationHandler; this.ambiguityResolver = ambiguityResolver; this.bindingResolver = bindingResolver; this.assigner = assigner; } /** * Delegates any intercepted method to invoke a {@code static} method that is declared by the supplied type. To be considered * a valid delegation target, the target method must be visible and accessible to the instrumented type. This is the case if * the target type is either public or in the same package as the instrumented type and if the target method is either public * or non-private and in the same package as the instrumented type. Private methods can only be used as a delegation target if * the interception is targeting the instrumented type. * * @param type The target type for the delegation. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Class type) { return withDefaultConfiguration().to(type); } /** * Delegates any intercepted method to invoke a {@code static} method that is declared by the supplied type. To be considered * a valid delegation target, the target method must be visible and accessible to the instrumented type. This is the case if * the target type is either public or in the same package as the instrumented type and if the target method is either public * or non-private and in the same package as the instrumented type. Private methods can only be used as a delegation target if * the interception is targeting the instrumented type. * * @param typeDescription The target type for the delegation. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(TypeDescription typeDescription) { return withDefaultConfiguration().to(typeDescription); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target) { return withDefaultConfiguration().to(target); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().to(target, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param fieldName The name of the field that is holding the {@code target} instance. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, String fieldName) { return withDefaultConfiguration().to(target, fieldName); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param fieldName The name of the field that is holding the {@code target} instance. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, String fieldName, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().to(target, fieldName, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, Type type) { return withDefaultConfiguration().to(target, type); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, Type type, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().to(target, type, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, Type type, String fieldName) { return withDefaultConfiguration().to(target, type, fieldName); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, Type type, String fieldName, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().to(target, type, fieldName, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, TypeDefinition typeDefinition) { return withDefaultConfiguration().to(target, typeDefinition); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, TypeDefinition typeDefinition, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().to(target, typeDefinition, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, TypeDefinition typeDefinition, String fieldName) { return withDefaultConfiguration().to(target, typeDefinition, fieldName); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public static MethodDelegation to(Object target, TypeDefinition typeDefinition, String fieldName, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().to(target, typeDefinition, fieldName, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a constructor of the supplied type. To be considered a valid delegation target, * a constructor must be visible and accessible to the instrumented type. This is the case if the constructor's declaring type is * either public or in the same package as the instrumented type and if the constructor is either public or non-private and in * the same package as the instrumented type. Private constructors can only be used as a delegation target if the delegation is * targeting the instrumented type. * * @param type The type to construct. * @return A delegation that redirects method calls to a constructor of the supplied type. */ public static MethodDelegation toConstructor(Class type) { return withDefaultConfiguration().toConstructor(type); } /** * Delegates any intercepted method to invoke a constructor of the supplied type. To be considered a valid delegation target, * a constructor must be visible and accessible to the instrumented type. This is the case if the constructor's declaring type is * either public or in the same package as the instrumented type and if the constructor is either public or non-private and in * the same package as the instrumented type. Private constructors can only be used as a delegation target if the delegation is * targeting the instrumented type. * * @param typeDescription The type to construct. * @return A delegation that redirects method calls to a constructor of the supplied type. */ public static MethodDelegation toConstructor(TypeDescription typeDescription) { return withDefaultConfiguration().toConstructor(typeDescription); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public static MethodDelegation toField(String name) { return withDefaultConfiguration().toField(name); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @param fieldLocatorFactory The field locator factory to use. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public static MethodDelegation toField(String name, FieldLocator.Factory fieldLocatorFactory) { return withDefaultConfiguration().toField(name, fieldLocatorFactory); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @param methodGraphCompiler The method graph compiler to use. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public static MethodDelegation toField(String name, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().toField(name, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @param fieldLocatorFactory The field locator factory to use. * @param methodGraphCompiler The method graph compiler to use. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public static MethodDelegation toField(String name, FieldLocator.Factory fieldLocatorFactory, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().toField(name, fieldLocatorFactory, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a method on an instance that is returned by a parameterless method of the * given name. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if * the method is either public or non-private and in the same package as the instrumented type. Private methods can only * be used as a delegation target if the delegation is targeting the instrumented type. * * @param name The name of the method that returns the delegation target. * @return A delegation that redirects invocations to the return value of a method that is declared by the instrumented type. */ public static MethodDelegation toMethodReturnOf(String name) { return withDefaultConfiguration().toMethodReturnOf(name); } /** * Delegates any intercepted method to invoke a method on an instance that is returned by a parameterless method of the * given name. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if * the method is either public or non-private and in the same package as the instrumented type. Private methods can only * be used as a delegation target if the delegation is targeting the instrumented type. * * @param name The name of the method that returns the delegation target. * @param methodGraphCompiler The method graph compiler to use. * @return A delegation that redirects invocations to the return value of a method that is declared by the instrumented type. */ public static MethodDelegation toMethodReturnOf(String name, MethodGraph.Compiler methodGraphCompiler) { return withDefaultConfiguration().toMethodReturnOf(name, methodGraphCompiler); } /** * Creates a configuration builder for a method delegation that is pre-configured with the ambiguity resolvers defined by * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver#DEFAULT} and the parameter binders * defined by {@link net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder.ParameterBinder#DEFAULTS}. * * @return A method delegation configuration with pre-configuration. */ public static WithCustomProperties withDefaultConfiguration() { return new WithCustomProperties(MethodDelegationBinder.AmbiguityResolver.DEFAULT, TargetMethodAnnotationDrivenBinder.ParameterBinder.DEFAULTS); } /** * Creates a configuration builder for a method delegation that does not apply any pre-configured * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}s or * {@link net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder.ParameterBinder}s. * * @return A method delegation configuration without any pre-configuration. */ public static WithCustomProperties withEmptyConfiguration() { return new WithCustomProperties(MethodDelegationBinder.AmbiguityResolver.NoOp.INSTANCE, Collections.>emptyList()); } /** * Applies an assigner to the method delegation that is used for assigning method return and parameter types. * * @param assigner The assigner to apply. * @return A method delegation implementation that makes use of the given designer. */ public Implementation.Composable withAssigner(Assigner assigner) { return new MethodDelegation(implementationDelegate, parameterBinders, ambiguityResolver, terminationHandler, bindingResolver, assigner); } /** * {@inheritDoc} */ public Implementation andThen(Implementation implementation) { return new Compound(new MethodDelegation(implementationDelegate, parameterBinders, ambiguityResolver, MethodDelegationBinder.TerminationHandler.Default.DROPPING, bindingResolver, assigner), implementation); } /** * {@inheritDoc} */ public Composable andThen(Composable implementation) { return new Compound.Composable(new MethodDelegation(implementationDelegate, parameterBinders, ambiguityResolver, MethodDelegationBinder.TerminationHandler.Default.DROPPING, bindingResolver, assigner), implementation); } /** * {@inheritDoc} */ public InstrumentedType prepare(InstrumentedType instrumentedType) { return implementationDelegate.prepare(instrumentedType); } /** * {@inheritDoc} */ public ByteCodeAppender appender(Target implementationTarget) { ImplementationDelegate.Compiled compiled = implementationDelegate.compile(implementationTarget.getInstrumentedType()); return new Appender(implementationTarget, new MethodDelegationBinder.Processor(compiled.getRecords(), ambiguityResolver, bindingResolver), terminationHandler, assigner, compiled); } /** * An implementation delegate is responsible for executing the actual method delegation and for resolving the target methods. */ protected interface ImplementationDelegate extends InstrumentedType.Prepareable { /** * A name prefix for fields. */ String FIELD_NAME_PREFIX = "delegate"; /** * Compiles this implementation delegate. * * @param instrumentedType The instrumented type. * @return A compiled implementation delegate. */ Compiled compile(TypeDescription instrumentedType); /** * A compiled implementation delegate. */ interface Compiled { /** * Resolves a stack manipulation that prepares the delegation invocation. * * @param instrumentedMethod The instrumented method. * @return A stack manipulation that is applied prior to loading arguments and executing the method call. */ StackManipulation prepare(MethodDescription instrumentedMethod); /** * Resolves an invoker to use for invoking the delegation target. * * @return The method invoker to use. */ MethodDelegationBinder.MethodInvoker invoke(); /** * Returns a list of binding records to consider for delegation. * * @return A list of delegation binder records to consider. */ List getRecords(); /** * A compiled implementation delegate for invoking a static method. */ @HashCodeAndEqualsPlugin.Enhance class ForStaticCall implements Compiled { /** * The list of records to consider. */ private final List records; /** * Creates a new compiled implementation delegate for a static method call. * * @param records The list of records to consider. */ protected ForStaticCall(List records) { this.records = records; } /** * {@inheritDoc} */ public StackManipulation prepare(MethodDescription instrumentedMethod) { return StackManipulation.Trivial.INSTANCE; } /** * {@inheritDoc} */ public MethodDelegationBinder.MethodInvoker invoke() { return MethodDelegationBinder.MethodInvoker.Simple.INSTANCE; } /** * {@inheritDoc} */ public List getRecords() { return records; } } /** * A compiled implementation delegate that invokes methods on a field. */ @HashCodeAndEqualsPlugin.Enhance class ForField implements Compiled { /** * The field to delegate to. */ private final FieldDescription fieldDescription; /** * The records to consider for delegation. */ private final List records; /** * Creates a new compiled implementation delegate for a field delegation. * * @param fieldDescription The field to delegate to. * @param records The records to consider for delegation. */ protected ForField(FieldDescription fieldDescription, List records) { this.fieldDescription = fieldDescription; this.records = records; } /** * {@inheritDoc} */ public StackManipulation prepare(MethodDescription instrumentedMethod) { if (instrumentedMethod.isStatic() && !fieldDescription.isStatic()) { throw new IllegalStateException("Cannot read " + fieldDescription + " from " + instrumentedMethod); } return new StackManipulation.Compound(fieldDescription.isStatic() ? StackManipulation.Trivial.INSTANCE : MethodVariableAccess.loadThis(), FieldAccess.forField(fieldDescription).read()); } /** * {@inheritDoc} */ public MethodDelegationBinder.MethodInvoker invoke() { return new MethodDelegationBinder.MethodInvoker.Virtual(fieldDescription.getType().asErasure()); } /** * {@inheritDoc} */ public List getRecords() { return records; } } /** * A compiled implementation delegate that invokes a method on an instance that is returned by another method. */ @HashCodeAndEqualsPlugin.Enhance class ForMethodReturn implements Compiled { /** * The method to call for result. */ private final MethodDescription methodDescription; /** * The records to consider for delegation. */ private final List records; /** * Creates a new compiled implementation delegate for a field delegation. * * @param methodDescription The method to call for result. * @param records The records to consider for delegation. */ protected ForMethodReturn(MethodDescription methodDescription, List records) { this.methodDescription = methodDescription; this.records = records; } /** * {@inheritDoc} */ public StackManipulation prepare(MethodDescription instrumentedMethod) { if (instrumentedMethod.isStatic() && !methodDescription.isStatic()) { throw new IllegalStateException("Cannot invoke " + methodDescription + " from " + instrumentedMethod); } return new StackManipulation.Compound(methodDescription.isStatic() ? StackManipulation.Trivial.INSTANCE : MethodVariableAccess.loadThis(), MethodInvocation.invoke(methodDescription)); } /** * {@inheritDoc} */ public MethodDelegationBinder.MethodInvoker invoke() { return new MethodDelegationBinder.MethodInvoker.Virtual(methodDescription.getReturnType().asErasure()); } /** * {@inheritDoc} */ public List getRecords() { return records; } } /** * A compiled implementation delegate for a constructor delegation. */ @HashCodeAndEqualsPlugin.Enhance class ForConstruction implements Compiled { /** * The type to be constructed. */ private final TypeDescription typeDescription; /** * The records to consider for delegation. */ private final List records; /** * Creates a new compiled implementation delegate for a constructor delegation. * * @param typeDescription The type to be constructed. * @param records The records to consider for delegation. */ protected ForConstruction(TypeDescription typeDescription, List records) { this.typeDescription = typeDescription; this.records = records; } /** * {@inheritDoc} */ public StackManipulation prepare(MethodDescription instrumentedMethod) { return new StackManipulation.Compound(TypeCreation.of(typeDescription), Duplication.SINGLE); } /** * {@inheritDoc} */ public MethodDelegationBinder.MethodInvoker invoke() { return MethodDelegationBinder.MethodInvoker.Simple.INSTANCE; } /** * {@inheritDoc} */ public List getRecords() { return records; } } } /** * An implementation delegate for a static method delegation. */ @HashCodeAndEqualsPlugin.Enhance class ForStaticMethod implements ImplementationDelegate { /** * The precompiled records. */ private final List records; /** * Creates a new implementation delegate for a static method delegation. * * @param records The precompiled record. */ protected ForStaticMethod(List records) { this.records = records; } /** * Precompiles a static method delegation for a given list of methods. * * @param methods The methods to consider. * @param methodDelegationBinder The method delegation binder to use. * @return An appropriate implementation delegate. */ protected static ImplementationDelegate of(MethodList methods, MethodDelegationBinder methodDelegationBinder) { List records = new ArrayList(methods.size()); for (MethodDescription methodDescription : methods) { records.add(methodDelegationBinder.compile(methodDescription)); } return new ForStaticMethod(records); } /** * {@inheritDoc} */ public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } /** * {@inheritDoc} */ public ImplementationDelegate.Compiled compile(TypeDescription instrumentedType) { return new Compiled.ForStaticCall(records); } } /** * An implementation delegate for invoking methods on a field that is declared by the instrumented type or a super type. */ @HashCodeAndEqualsPlugin.Enhance abstract class ForField implements ImplementationDelegate { /** * The name of the field that is target of the delegation. */ protected final String fieldName; /** * The method graph compiler to use. */ protected final MethodGraph.Compiler methodGraphCompiler; /** * The parameter binders to use. */ protected final List> parameterBinders; /** * The matcher to use for filtering methods. */ protected final ElementMatcher matcher; /** * Creates a new implementation delegate for a field delegation. * * @param fieldName The name of the field that is target of the delegation. * @param methodGraphCompiler The method graph compiler to use. * @param parameterBinders The parameter binders to use. * @param matcher The matcher to use for filtering methods. */ protected ForField(String fieldName, MethodGraph.Compiler methodGraphCompiler, List> parameterBinders, ElementMatcher matcher) { this.fieldName = fieldName; this.methodGraphCompiler = methodGraphCompiler; this.parameterBinders = parameterBinders; this.matcher = matcher; } /** * {@inheritDoc} */ public Compiled compile(TypeDescription instrumentedType) { FieldDescription fieldDescription = resolve(instrumentedType); if (!fieldDescription.getType().asErasure().isVisibleTo(instrumentedType)) { throw new IllegalStateException(fieldDescription + " is not visible to " + instrumentedType); } else { MethodList candidates = methodGraphCompiler.compile(fieldDescription.getType(), instrumentedType) .listNodes() .asMethodList() .filter(matcher); List records = new ArrayList(candidates.size()); MethodDelegationBinder methodDelegationBinder = TargetMethodAnnotationDrivenBinder.of(parameterBinders); for (MethodDescription candidate : candidates) { records.add(methodDelegationBinder.compile(candidate)); } return new Compiled.ForField(fieldDescription, records); } } /** * Resolves the field to which is delegated. * * @param instrumentedType The instrumented type. * @return The field that is the delegation target. */ protected abstract FieldDescription resolve(TypeDescription instrumentedType); /** * An implementation target for a static field that is declared by the instrumented type and that is assigned an instance. */ @HashCodeAndEqualsPlugin.Enhance protected static class WithInstance extends ForField { /** * The target instance. */ private final Object target; /** * The field's type. */ private final TypeDescription.Generic fieldType; /** * Creates a new implementation delegate for invoking methods on a supplied instance. * * @param fieldName The name of the field that is target of the delegation. * @param methodGraphCompiler The method graph compiler to use. * @param parameterBinders The parameter binders to use. * @param matcher The matcher to use for filtering methods. * @param target The target instance. * @param fieldType The field's type. */ protected WithInstance(String fieldName, MethodGraph.Compiler methodGraphCompiler, List> parameterBinders, ElementMatcher matcher, Object target, TypeDescription.Generic fieldType) { super(fieldName, methodGraphCompiler, parameterBinders, matcher); this.target = target; this.fieldType = fieldType; } /** * {@inheritDoc} */ public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType.withAuxiliaryField(new FieldDescription.Token(fieldName, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_VOLATILE | Opcodes.ACC_SYNTHETIC, fieldType), target); } @Override protected FieldDescription resolve(TypeDescription instrumentedType) { if (!fieldType.asErasure().isVisibleTo(instrumentedType)) { throw new IllegalStateException(fieldType + " is not visible to " + instrumentedType); } else { return instrumentedType.getDeclaredFields() .filter(named(fieldName).and(fieldType(fieldType.asErasure()))) .getOnly(); } } } /** * An implementation target for a field that is declared by the instrumented type or a super type. */ @HashCodeAndEqualsPlugin.Enhance protected static class WithLookup extends ForField { /** * The field locator factory to use for locating the field to delegate to. */ private final FieldLocator.Factory fieldLocatorFactory; /** * Creates a new implementation delegate for a field that is declared by the instrumented type or any super type. * * @param fieldName The name of the field that is target of the delegation. * @param methodGraphCompiler The method graph compiler to use. * @param parameterBinders The parameter binders to use. * @param matcher The matcher to use for filtering methods. * @param fieldLocatorFactory The field locator factory to use for locating the field to delegate to. */ protected WithLookup(String fieldName, MethodGraph.Compiler methodGraphCompiler, List> parameterBinders, ElementMatcher matcher, FieldLocator.Factory fieldLocatorFactory) { super(fieldName, methodGraphCompiler, parameterBinders, matcher); this.fieldLocatorFactory = fieldLocatorFactory; } /** * {@inheritDoc} */ public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } @Override protected FieldDescription resolve(TypeDescription instrumentedType) { FieldLocator.Resolution resolution = fieldLocatorFactory.make(instrumentedType).locate(fieldName); if (!resolution.isResolved()) { throw new IllegalStateException("Could not locate " + fieldName + " on " + instrumentedType); } else { return resolution.getField(); } } } } /** * An implementation delegate for invoking a delegation target on the another methods return value. */ @HashCodeAndEqualsPlugin.Enhance class ForMethodReturn implements ImplementationDelegate { /** * The name of the method to invoke. */ private final String name; /** * The method graph compiler to use. */ private final MethodGraph.Compiler methodGraphCompiler; /** * The parameter binders to use. */ private final List> parameterBinders; /** * The matcher to use for filtering methods. */ private final ElementMatcher matcher; /** * Creates a new implementation delegate for a method return value delegation. * * @param name The name of the method to invoke. * @param methodGraphCompiler The method graph compiler to use. * @param parameterBinders The parameter binders to use. * @param matcher The matcher to use for filtering methods. */ protected ForMethodReturn(String name, MethodGraph.Compiler methodGraphCompiler, List> parameterBinders, ElementMatcher matcher) { this.name = name; this.methodGraphCompiler = methodGraphCompiler; this.parameterBinders = parameterBinders; this.matcher = matcher; } /** * {@inheritDoc} */ public Compiled compile(TypeDescription instrumentedType) { MethodList targets = new MethodList.Explicit(CompoundList.of( instrumentedType.getDeclaredMethods().filter(isStatic().or(isPrivate())), methodGraphCompiler.compile((TypeDefinition) instrumentedType).listNodes().asMethodList()) ).filter(named(name).and(takesArguments(0)).and(not(returns(isPrimitive().or(isArray()))))); if (targets.size() != 1) { throw new IllegalStateException(instrumentedType + " does not define method without arguments with name " + name + ": " + targets); } else if (!targets.getOnly().getReturnType().asErasure().isVisibleTo(instrumentedType)) { throw new IllegalStateException(targets.getOnly() + " is not visible to " + instrumentedType); } else { MethodList candidates = methodGraphCompiler.compile(targets.getOnly().getReturnType(), instrumentedType) .listNodes() .asMethodList() .filter(matcher); List records = new ArrayList(candidates.size()); MethodDelegationBinder methodDelegationBinder = TargetMethodAnnotationDrivenBinder.of(parameterBinders); for (MethodDescription candidate : candidates) { records.add(methodDelegationBinder.compile(candidate)); } return new Compiled.ForMethodReturn(targets.get(0), records); } } /** * {@inheritDoc} */ public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } } /** * An implementation delegate for constructing an instance. */ @HashCodeAndEqualsPlugin.Enhance class ForConstruction implements ImplementationDelegate { /** * The type being constructed. */ private final TypeDescription typeDescription; /** * The precompiled delegation records. */ private final List records; /** * Creates an implementation delegate for constructing a new instance. * * @param typeDescription The type being constructed. * @param records The precompiled delegation records. */ protected ForConstruction(TypeDescription typeDescription, List records) { this.typeDescription = typeDescription; this.records = records; } /** * Creates an implementation delegate for constructing a new instance. * * @param typeDescription The type being constructed. * @param methods The constructors to consider. * @param methodDelegationBinder The method delegation binder to use. * @return An appropriate implementation delegate. */ protected static ImplementationDelegate of(TypeDescription typeDescription, MethodList methods, MethodDelegationBinder methodDelegationBinder) { List records = new ArrayList(methods.size()); for (MethodDescription methodDescription : methods) { records.add(methodDelegationBinder.compile(methodDescription)); } return new ForConstruction(typeDescription, records); } /** * {@inheritDoc} */ public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } /** * {@inheritDoc} */ public Compiled compile(TypeDescription instrumentedType) { return new Compiled.ForConstruction(typeDescription, records); } } } /** * The appender for implementing a {@link net.bytebuddy.implementation.MethodDelegation}. */ @HashCodeAndEqualsPlugin.Enhance protected static class Appender implements ByteCodeAppender { /** * The implementation target of this implementation. */ private final Target implementationTarget; /** * The method delegation binder processor which is responsible for implementing the method delegation. */ private final MethodDelegationBinder.Record processor; /** * A termination handler for a method delegation binder. */ private final MethodDelegationBinder.TerminationHandler terminationHandler; /** * The assigner to use. */ private final Assigner assigner; /** * The compiled implementation delegate. */ private final ImplementationDelegate.Compiled compiled; /** * Creates a new appender for a method delegation. * * @param implementationTarget The implementation target of this implementation. * @param processor The method delegation binder processor which is responsible for implementing the method delegation. * @param terminationHandler A termination handler for a method delegation binder. * @param assigner The assigner to use. * @param compiled The compiled implementation delegate. */ protected Appender(Target implementationTarget, MethodDelegationBinder.Record processor, MethodDelegationBinder.TerminationHandler terminationHandler, Assigner assigner, ImplementationDelegate.Compiled compiled) { this.implementationTarget = implementationTarget; this.processor = processor; this.terminationHandler = terminationHandler; this.assigner = assigner; this.compiled = compiled; } /** * {@inheritDoc} */ public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { StackManipulation.Size stackSize = new StackManipulation.Compound( compiled.prepare(instrumentedMethod), processor.bind(implementationTarget, instrumentedMethod, terminationHandler, compiled.invoke(), assigner) ).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } } /** * A {@link MethodDelegation} with custom configuration. */ @HashCodeAndEqualsPlugin.Enhance public static class WithCustomProperties { /** * The ambiguity resolver to use. */ private final MethodDelegationBinder.AmbiguityResolver ambiguityResolver; /** * The parameter binders to use. */ private final List> parameterBinders; /** * The binding resolver being used to select the relevant method binding. */ private final MethodDelegationBinder.BindingResolver bindingResolver; /** * The matcher to use for filtering relevant methods. */ private final ElementMatcher matcher; /** * Creates a new method delegation with custom properties that does not filter any methods. * * @param ambiguityResolver The ambiguity resolver to use. * @param parameterBinders The parameter binders to use. */ protected WithCustomProperties(MethodDelegationBinder.AmbiguityResolver ambiguityResolver, List> parameterBinders) { this(ambiguityResolver, parameterBinders, MethodDelegationBinder.BindingResolver.Default.INSTANCE, any()); } /** * Creates a new method delegation with custom properties. * * @param ambiguityResolver The ambiguity resolver to use. * @param parameterBinders The parameter binders to use. * @param bindingResolver The binding resolver being used to select the relevant method binding. * @param matcher The matcher to use for filtering relevant methods. */ private WithCustomProperties(MethodDelegationBinder.AmbiguityResolver ambiguityResolver, List> parameterBinders, MethodDelegationBinder.BindingResolver bindingResolver, ElementMatcher matcher) { this.ambiguityResolver = ambiguityResolver; this.parameterBinders = parameterBinders; this.bindingResolver = bindingResolver; this.matcher = matcher; } /** * Configures this method delegation to use the supplied ambiguity resolvers when deciding which out of two ore * more legal delegation targets should be considered. * * @param ambiguityResolver The ambiguity resolvers to use in their application order. * @return A new delegation configuration which also applies the supplied ambiguity resolvers. */ public WithCustomProperties withResolvers(MethodDelegationBinder.AmbiguityResolver... ambiguityResolver) { return withResolvers(Arrays.asList(ambiguityResolver)); } /** * Configures this method delegation to use the supplied ambiguity resolvers when deciding which out of two ore * more legal delegation targets should be considered. * * @param ambiguityResolvers The ambiguity resolvers to use in their application order. * @return A new delegation configuration which also applies the supplied ambiguity resolvers. */ public WithCustomProperties withResolvers(List ambiguityResolvers) { return new WithCustomProperties(new MethodDelegationBinder.AmbiguityResolver.Compound(CompoundList.of(this.ambiguityResolver, ambiguityResolvers)), parameterBinders, bindingResolver, matcher); } /** * Configures this method delegation to use the supplied parameter binders when deciding what value to assign to * a parameter of a delegation target. * * @param parameterBinder The parameter binders to use. * @return A new delegation configuration which also applies the supplied parameter binders. */ public WithCustomProperties withBinders(TargetMethodAnnotationDrivenBinder.ParameterBinder... parameterBinder) { return withBinders(Arrays.asList(parameterBinder)); } /** * Configures this method delegation to use the supplied parameter binders when deciding what value to assign to * a parameter of a delegation target. * * @param parameterBinders The parameter binders to use. * @return A new delegation configuration which also applies the supplied parameter binders. */ public WithCustomProperties withBinders(List> parameterBinders) { return new WithCustomProperties(ambiguityResolver, CompoundList.of(this.parameterBinders, parameterBinders), bindingResolver, matcher); } /** * Configures a custom binding resolver which is responsible for choosing a method binding among multiple candidates. Configuring * a resolver overrides any previous configuration. * * @param bindingResolver The binding resolver being used to select the relevant method binding. * @return A new delegation configuration which applies the supplied binding resolver. */ public WithCustomProperties withBindingResolver(MethodDelegationBinder.BindingResolver bindingResolver) { return new WithCustomProperties(ambiguityResolver, parameterBinders, bindingResolver, matcher); } /** * Configures this method delegation to only consider methods or constructors as a delegation target if they match the supplied matcher. * * @param matcher The matcher any delegation target needs to match in order to be considered a for delegation. * @return A new delegation configuration which only considers methods for delegation if they match the supplied matcher. */ @SuppressWarnings("unchecked") public WithCustomProperties filter(ElementMatcher matcher) { return new WithCustomProperties(ambiguityResolver, parameterBinders, bindingResolver, new ElementMatcher.Junction.Conjunction(this.matcher, matcher)); } /** * Delegates any intercepted method to invoke a {@code static} method that is declared by the supplied type. To be considered * a valid delegation target, the target method must be visible and accessible to the instrumented type. This is the case if * the target type is either public or in the same package as the instrumented type and if the target method is either public * or non-private and in the same package as the instrumented type. Private methods can only be used as a delegation target if * the interception is targeting the instrumented type. * * @param type The target type for the delegation. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Class type) { return to(TypeDescription.ForLoadedType.of(type)); } /** * Delegates any intercepted method to invoke a {@code static} method that is declared by the supplied type. To be considered * a valid delegation target, the target method must be visible and accessible to the instrumented type. This is the case if * the target type is either public or in the same package as the instrumented type and if the target method is either public * or non-private and in the same package as the instrumented type. Private methods can only be used as a delegation target if * the delegation is targeting the instrumented type. * * @param typeDescription The target type for the delegation. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(TypeDescription typeDescription) { if (typeDescription.isArray()) { throw new IllegalArgumentException("Cannot delegate to array " + typeDescription); } else if (typeDescription.isPrimitive()) { throw new IllegalArgumentException("Cannot delegate to primitive " + typeDescription); } return new MethodDelegation(ImplementationDelegate.ForStaticMethod.of(typeDescription.getDeclaredMethods().filter(isStatic().and(matcher)), TargetMethodAnnotationDrivenBinder.of(parameterBinders)), parameterBinders, ambiguityResolver, bindingResolver); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target) { return to(target, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, MethodGraph.Compiler methodGraphCompiler) { return to(target, target.getClass(), methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param fieldName The name of the field that is holding the {@code target} instance. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, String fieldName) { return to(target, fieldName, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param fieldName The name of the field that is holding the {@code target} instance. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, String fieldName, MethodGraph.Compiler methodGraphCompiler) { return to(target, target.getClass(), fieldName, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, Type type) { return to(target, type, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, Type type, MethodGraph.Compiler methodGraphCompiler) { return to(target, type, ImplementationDelegate.FIELD_NAME_PREFIX + "$" + RandomString.hashOf(target), methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, Type type, String fieldName) { return to(target, type, fieldName, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param type The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, Type type, String fieldName, MethodGraph.Compiler methodGraphCompiler) { return to(target, TypeDefinition.Sort.describe(type), fieldName, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, TypeDefinition typeDefinition) { return to(target, typeDefinition, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, TypeDefinition typeDefinition, MethodGraph.Compiler methodGraphCompiler) { return to(target, typeDefinition, ImplementationDelegate.FIELD_NAME_PREFIX + "$" + RandomString.hashOf(target), methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, TypeDefinition typeDefinition, String fieldName) { return to(target, typeDefinition, fieldName, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a non-{@code static} method that is declared by the supplied type's instance or any * of its super types. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param target The target instance for the delegation. * @param typeDefinition The most specific type of which {@code target} should be considered. Must be a super type of the target's actual type. * @param fieldName The name of the field that is holding the {@code target} instance. * @param methodGraphCompiler The method graph compiler to use. * @return A method delegation that redirects method calls to a static method of the supplied type. */ public MethodDelegation to(Object target, TypeDefinition typeDefinition, String fieldName, MethodGraph.Compiler methodGraphCompiler) { if (!typeDefinition.asErasure().isInstance(target)) { throw new IllegalArgumentException(target + " is not an instance of " + typeDefinition); } return new MethodDelegation(new ImplementationDelegate.ForField.WithInstance(fieldName, methodGraphCompiler, parameterBinders, matcher, target, typeDefinition.asGenericType()), parameterBinders, ambiguityResolver, bindingResolver); } /** * Delegates any intercepted method to invoke a constructor of the supplied type. To be considered a valid delegation target, * a constructor must be visible and accessible to the instrumented type. This is the case if the constructor's declaring type is * either public or in the same package as the instrumented type and if the constructor is either public or non-private and in * the same package as the instrumented type. Private constructors can only be used as a delegation target if the delegation is * targeting the instrumented type. * * @param type The type to construct. * @return A delegation that redirects method calls to a constructor of the supplied type. */ public MethodDelegation toConstructor(Class type) { return toConstructor(TypeDescription.ForLoadedType.of(type)); } /** * Delegates any intercepted method to invoke a constructor of the supplied type. To be considered a valid delegation target, * a constructor must be visible and accessible to the instrumented type. This is the case if the constructor's declaring type is * either public or in the same package as the instrumented type and if the constructor is either public or non-private and in * the same package as the instrumented type. Private constructors can only be used as a delegation target if the delegation is * targeting the instrumented type. * * @param typeDescription The type to construct. * @return A delegation that redirects method calls to a constructor of the supplied type. */ public MethodDelegation toConstructor(TypeDescription typeDescription) { return new MethodDelegation(ImplementationDelegate.ForConstruction.of(typeDescription, typeDescription.getDeclaredMethods().filter(isConstructor().and(matcher)), TargetMethodAnnotationDrivenBinder.of(parameterBinders)), parameterBinders, ambiguityResolver, bindingResolver); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public MethodDelegation toField(String name) { return toField(name, FieldLocator.ForClassHierarchy.Factory.INSTANCE); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @param fieldLocatorFactory The field locator factory to use. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public MethodDelegation toField(String name, FieldLocator.Factory fieldLocatorFactory) { return toField(name, fieldLocatorFactory, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @param methodGraphCompiler The method graph compiler to use. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public MethodDelegation toField(String name, MethodGraph.Compiler methodGraphCompiler) { return toField(name, FieldLocator.ForClassHierarchy.Factory.INSTANCE, methodGraphCompiler); } /** * Delegates any intercepted method to invoke a non-{@code static} method on the instance of the supplied field. To be * considered a valid delegation target, a method must be visible and accessible to the instrumented type. This is the * case if the method's declaring type is either public or in the same package as the instrumented type and if the method * is either public or non-private and in the same package as the instrumented type. Private methods can only be used as * a delegation target if the delegation is targeting the instrumented type. * * @param name The field's name. * @param fieldLocatorFactory The field locator factory to use. * @param methodGraphCompiler The method graph compiler to use. * @return A delegation that redirects invocations to a method of the specified field's instance. */ public MethodDelegation toField(String name, FieldLocator.Factory fieldLocatorFactory, MethodGraph.Compiler methodGraphCompiler) { return new MethodDelegation(new ImplementationDelegate.ForField.WithLookup(name, methodGraphCompiler, parameterBinders, matcher, fieldLocatorFactory), parameterBinders, ambiguityResolver, bindingResolver); } /** * Delegates any intercepted method to invoke a method on an instance that is returned by a parameterless method of the * given name. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if * the method is either public or non-private and in the same package as the instrumented type. Private methods can only * be used as a delegation target if the delegation is targeting the instrumented type. * * @param name The name of the method that returns the delegation target. * @return A delegation that redirects invocations to the return value of a method that is declared by the instrumented type. */ public MethodDelegation toMethodReturnOf(String name) { return toMethodReturnOf(name, MethodGraph.Compiler.DEFAULT); } /** * Delegates any intercepted method to invoke a method on an instance that is returned by a parameterless method of the * given name. To be considered a valid delegation target, a method must be visible and accessible to the instrumented type. * This is the case if the method's declaring type is either public or in the same package as the instrumented type and if * the method is either public or non-private and in the same package as the instrumented type. Private methods can only * be used as a delegation target if the delegation is targeting the instrumented type. * * @param name The name of the method that returns the delegation target. * @param methodGraphCompiler The method graph compiler to use. * @return A delegation that redirects invocations to the return value of a method that is declared by the instrumented type. */ public MethodDelegation toMethodReturnOf(String name, MethodGraph.Compiler methodGraphCompiler) { return new MethodDelegation(new ImplementationDelegate.ForMethodReturn(name, methodGraphCompiler, parameterBinders, matcher), parameterBinders, ambiguityResolver, bindingResolver); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy