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

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bind.annotation.BindingPriority;
import net.bytebuddy.implementation.bytecode.Removal;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.utility.CompoundList;
import org.objectweb.asm.MethodVisitor;

import java.io.PrintStream;
import java.util.*;

A method delegation binder is responsible for creating a method binding for a source method to a target method. Such a binding allows to implement the source method by calling the target method.

 

Usually, an implementation will attempt to bind a specific source method to a set of target method candidates where all legal bindings are considered for binding. To chose a specific candidate, an AmbiguityResolver will be consulted for selecting a best binding.
/** * A method delegation binder is responsible for creating a method binding for a <i>source method</i> to a * <i>target method</i>. Such a binding allows to implement the source method by calling the target method. * <p>&nbsp;</p> * Usually, an implementation will attempt to bind a specific source method to a set of target method candidates * where all legal bindings are considered for binding. To chose a specific candidate, an * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver} * will be consulted for selecting a <i>best</i> binding. */
public interface MethodDelegationBinder {
Compiles this method delegation binder for a target method.
Params:
  • candidate – The target method to bind.
Returns:A compiled target for binding.
/** * Compiles this method delegation binder for a target method. * * @param candidate The target method to bind. * @return A compiled target for binding. */
Record compile(MethodDescription candidate);
A method delegation that was compiled to a target method.
/** * A method delegation that was compiled to a target method. */
interface Record {
Attempts a binding of a source method to this compiled target.
Params:
  • implementationTarget – The target of the current implementation onto which this binding is to be applied.
  • source – The method that is to be bound to the target method.
  • terminationHandler – The termination handler to apply.
  • methodInvoker – The method invoker to use.
  • assigner – The assigner to use.
Returns:A binding representing this attempt to bind the source method to the target method.
/** * Attempts a binding of a source method to this compiled target. * * @param implementationTarget The target of the current implementation onto which this binding is to be applied. * @param source The method that is to be bound to the {@code target} method. * @param terminationHandler The termination handler to apply. * @param methodInvoker The method invoker to use. * @param assigner The assigner to use. * @return A binding representing this attempt to bind the {@code source} method to the {@code target} method. */
MethodBinding bind(Implementation.Target implementationTarget, MethodDescription source, TerminationHandler terminationHandler, MethodInvoker methodInvoker, Assigner assigner);
A compiled method delegation binder that only yields illegal bindings.
/** * A compiled method delegation binder that only yields illegal bindings. */
enum Illegal implements Record {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public MethodBinding bind(Implementation.Target implementationTarget, MethodDescription source, TerminationHandler terminationHandler, MethodInvoker methodInvoker, Assigner assigner) { return MethodBinding.Illegal.INSTANCE; } } }
Implementations are used as delegates for invoking a method that was bound using a MethodDelegationBinder.
/** * Implementations are used as delegates for invoking a method that was bound * using a {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}. */
interface MethodInvoker {
Creates a method invocation for a given method.
Params:
  • methodDescription – The method to be invoked.
Returns:A stack manipulation encapsulating this method invocation.
/** * Creates a method invocation for a given method. * * @param methodDescription The method to be invoked. * @return A stack manipulation encapsulating this method invocation. */
StackManipulation invoke(MethodDescription methodDescription);
A simple method invocation that merely uses the most general form of method invocation as provided by MethodInvocation.
/** * A simple method invocation that merely uses the most general form of method invocation as provided by * {@link net.bytebuddy.implementation.bytecode.member.MethodInvocation}. */
enum Simple implements MethodInvoker {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation invoke(MethodDescription methodDescription) { return MethodInvocation.invoke(methodDescription); } }
A method invocation that enforces a virtual invocation that is dispatched on a given type.
/** * A method invocation that enforces a virtual invocation that is dispatched on a given type. */
@HashCodeAndEqualsPlugin.Enhance class Virtual implements MethodInvoker {
The type on which a method should be invoked virtually.
/** * The type on which a method should be invoked virtually. */
private final TypeDescription typeDescription;
Creates an immutable method invoker that dispatches all methods on a given type.
Params:
  • typeDescription – The type on which the method is invoked by virtual invocation.
/** * Creates an immutable method invoker that dispatches all methods on a given type. * * @param typeDescription The type on which the method is invoked by virtual invocation. */
public Virtual(TypeDescription typeDescription) { this.typeDescription = typeDescription; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation invoke(MethodDescription methodDescription) { return MethodInvocation.invoke(methodDescription).virtual(typeDescription); } } }
A binding attempt for a single parameter. Implementations of this type are a suggestion of composing a MethodBinding by using a Builder. However, method bindings can also be composed without this type which is merely a suggestion.
Type parameters:
  • <T> – The type of the identification token for this parameter binding.
/** * A binding attempt for a single parameter. Implementations of this type are a suggestion of composing a * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding} * by using a * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding.Builder}. * However, method bindings can also be composed without this type which is merely a suggestion. * * @param <T> The type of the identification token for this parameter binding. */
interface ParameterBinding<T> extends StackManipulation {
Returns an identification token for this binding.
Returns:An identification token unique to this binding.
/** * Returns an identification token for this binding. * * @return An identification token unique to this binding. */
T getIdentificationToken();
A singleton representation of an illegal binding for a method parameter. An illegal binding usually suggests that a source method cannot be bound to a specific target method.
/** * A singleton representation of an illegal binding for a method parameter. An illegal binding usually * suggests that a source method cannot be bound to a specific target method. */
enum Illegal implements ParameterBinding<Void> {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public Void getIdentificationToken() { throw new IllegalStateException("An illegal binding does not define an identification token"); }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { throw new IllegalStateException("An illegal parameter binding must not be applied"); } }
An anonymous binding of a target method parameter.
/** * An anonymous binding of a target method parameter. */
@HashCodeAndEqualsPlugin.Enhance class Anonymous implements ParameterBinding<Object> {
A pseudo-token that is not exposed and therefore anonymous.
/** * A pseudo-token that is not exposed and therefore anonymous. */
@HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.IGNORE) private final Object anonymousToken;
The stack manipulation that represents the loading of the parameter binding onto the stack.
/** * The stack manipulation that represents the loading of the parameter binding onto the stack. */
private final StackManipulation delegate;
Creates a new, anonymous parameter binding.
Params:
  • delegate – The stack manipulation that is responsible for loading the parameter value for this target method parameter onto the stack.
/** * Creates a new, anonymous parameter binding. * * @param delegate The stack manipulation that is responsible for loading the parameter value for this * target method parameter onto the stack. */
public Anonymous(StackManipulation delegate) { this.delegate = delegate; anonymousToken = new Object(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getIdentificationToken() { return anonymousToken; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return delegate.isValid(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { return delegate.apply(methodVisitor, implementationContext); } }
A uniquely identifiable parameter binding for a target method. Such bindings are usually later processed by a AmbiguityResolver in order to resolve binding conflicts between several bindable target methods to the same source method.
Type parameters:
  • <T> – The type of the identification token.
See Also:
/** * A uniquely identifiable parameter binding for a target method. Such bindings are usually later processed by * a {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver} * in order to resolve binding conflicts between several bindable target methods to the same source method. * * @param <T> The type of the identification token. * @see net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver */
@HashCodeAndEqualsPlugin.Enhance class Unique<T> implements ParameterBinding<T> {
The token that identifies this parameter binding as unique.
/** * The token that identifies this parameter binding as unique. */
private final T identificationToken;
The stack manipulation that represents the loading of the parameter binding onto the stack.
/** * The stack manipulation that represents the loading of the parameter binding onto the stack. */
private final StackManipulation delegate;
Creates a new unique parameter binding representant.
Params:
  • delegate – The stack manipulation that loads the argument for this parameter onto the operand stack.
  • identificationToken – The token used for identifying this parameter binding.
/** * Creates a new unique parameter binding representant. * * @param delegate The stack manipulation that loads the argument for this parameter onto the operand stack. * @param identificationToken The token used for identifying this parameter binding. */
public Unique(StackManipulation delegate, T identificationToken) { this.delegate = delegate; this.identificationToken = identificationToken; }
A factory method for creating a unique binding that infers the tokens type.
Params:
  • delegate – The stack manipulation delegate.
  • identificationToken – The identification token.
Type parameters:
  • <S> – The type of the identification token.
Returns:A new instance representing this unique binding.
/** * A factory method for creating a unique binding that infers the tokens type. * * @param delegate The stack manipulation delegate. * @param identificationToken The identification token. * @param <S> The type of the identification token. * @return A new instance representing this unique binding. */
public static <S> Unique<S> of(StackManipulation delegate, S identificationToken) { return new Unique<S>(delegate, identificationToken); }
{@inheritDoc}
/** * {@inheritDoc} */
public T getIdentificationToken() { return identificationToken; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return delegate.isValid(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { return delegate.apply(methodVisitor, implementationContext); } } }
A binding attempt created by a MethodDelegationBinder.
/** * A binding attempt created by a * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}. */
interface MethodBinding extends StackManipulation {
Returns the target method's parameter index for a given parameter binding token.

 

A binding token can be any object that implements valid Object.hashCode() and Object.equals(Object) methods in order to look up a given binding. This way, two bindings can be evaluated of having performed a similar type of binding such that these bindings can be compared and a dominant binding can be identified by an AmbiguityResolver. Furthermore, a binding is implicitly required to insure the uniqueness of such a parameter binding.
Params:
  • parameterBindingToken – A token which is used to identify a specific unique binding for a given parameter of the target method.
Returns:The target method's parameter index of this binding or null if no such argument binding was applied for this binding.
/** * Returns the target method's parameter index for a given parameter binding token. * <p>&nbsp;</p> * A binding token can be any object * that implements valid {@link Object#hashCode()} and {@link Object#equals(Object)} methods in order * to look up a given binding. This way, two bindings can be evaluated of having performed a similar type of * binding such that these bindings can be compared and a dominant binding can be identified by an * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}. * Furthermore, a binding is implicitly required to insure the uniqueness of such a parameter binding. * * @param parameterBindingToken A token which is used to identify a specific unique binding for a given parameter * of the target method. * @return The target method's parameter index of this binding or {@code null} if no such argument binding * was applied for this binding. */
Integer getTargetParameterIndex(Object parameterBindingToken);
Returns the target method of the method binding attempt.
Returns:The target method to which the
/** * Returns the target method of the method binding attempt. * * @return The target method to which the */
MethodDescription getTarget();
Representation of an attempt to bind a source method to a target method that is not applicable.
See Also:
  • MethodDelegationBinder
/** * Representation of an attempt to bind a source method to a target method that is not applicable. * * @see net.bytebuddy.implementation.bind.MethodDelegationBinder */
enum Illegal implements MethodBinding {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public Integer getTargetParameterIndex(Object parameterBindingToken) { throw new IllegalStateException("Method is not bound"); }
{@inheritDoc}
/** * {@inheritDoc} */
public MethodDescription getTarget() { throw new IllegalStateException("Method is not bound"); }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { throw new IllegalStateException("Cannot delegate to an unbound method"); } }
A mutable builder that allows to compose a MethodBinding by adding parameter bindings incrementally.
/** * A mutable builder that allows to compose a * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding} * by adding parameter bindings incrementally. */
class Builder {
The method invoker for invoking the actual method that is bound.
/** * The method invoker for invoking the actual method that is bound. */
private final MethodInvoker methodInvoker;
The target method that for which a binding is to be constructed by this builder..
/** * The target method that for which a binding is to be constructed by this builder.. */
private final MethodDescription candidate;
The current list of stack manipulations for loading values for each parameter onto the operand stack.
/** * The current list of stack manipulations for loading values for each parameter onto the operand stack. */
private final List<StackManipulation> parameterStackManipulations;
A mapping of identification tokens to the parameter index they were bound for.
/** * A mapping of identification tokens to the parameter index they were bound for. */
private final LinkedHashMap<Object, Integer> registeredTargetIndices;
The index of the next parameter that is to be bound.
/** * The index of the next parameter that is to be bound. */
private int nextParameterIndex;
Creates a new builder for the binding of a given method.
Params:
  • methodInvoker – The method invoker that is used to create the method invocation of the target method.
  • candidate – The target method that is target of the binding.
/** * Creates a new builder for the binding of a given method. * * @param methodInvoker The method invoker that is used to create the method invocation of the {@code target} method. * @param candidate The target method that is target of the binding. */
public Builder(MethodInvoker methodInvoker, MethodDescription candidate) { this.methodInvoker = methodInvoker; this.candidate = candidate; parameterStackManipulations = new ArrayList<StackManipulation>(candidate.getParameters().size()); registeredTargetIndices = new LinkedHashMap<Object, Integer>(); nextParameterIndex = 0; }
Appends a stack manipulation for the next parameter of the target method.
Params:
  • parameterBinding – A binding representing the next subsequent parameter of the method.
Returns:false if the parameterBindingToken was already bound. A conflicting binding should usually abort the attempt of binding a method and this Builder should be discarded.
/** * Appends a stack manipulation for the next parameter of the target method. * * @param parameterBinding A binding representing the next subsequent parameter of the method. * @return {@code false} if the {@code parameterBindingToken} was already bound. A conflicting binding should * usually abort the attempt of binding a method and this {@code Builder} should be discarded. */
public boolean append(ParameterBinding<?> parameterBinding) { parameterStackManipulations.add(parameterBinding); return registeredTargetIndices.put(parameterBinding.getIdentificationToken(), nextParameterIndex++) == null; }
Creates a binding that represents the bindings collected by this Builder.
Params:
  • terminatingManipulation – A stack manipulation that is applied after the method invocation.
Returns:A binding representing the parameter bindings collected by this builder.
/** * Creates a binding that represents the bindings collected by this {@code Builder}. * * @param terminatingManipulation A stack manipulation that is applied after the method invocation. * @return A binding representing the parameter bindings collected by this builder. */
public MethodBinding build(StackManipulation terminatingManipulation) { if (candidate.getParameters().size() != nextParameterIndex) { throw new IllegalStateException("The number of parameters bound does not equal the target's number of parameters"); } return new Build(candidate, registeredTargetIndices, methodInvoker.invoke(candidate), parameterStackManipulations, terminatingManipulation); }
A method binding that was created by a Builder.
/** * A method binding that was created by a * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding.Builder}. */
@HashCodeAndEqualsPlugin.Enhance protected static class Build implements MethodBinding {
The target method this binding represents.
/** * The target method this binding represents. */
private final MethodDescription target;
A map of identification tokens to the indices of their binding parameters.
/** * A map of identification tokens to the indices of their binding parameters. */
private final Map<?, Integer> registeredTargetIndices;
A stack manipulation that represents the actual method invocation.
/** * A stack manipulation that represents the actual method invocation. */
private final StackManipulation methodInvocation;
A list of manipulations that each represent the loading of a parameter value onto the operand stack.
/** * A list of manipulations that each represent the loading of a parameter value onto the operand stack. */
private final List<StackManipulation> parameterStackManipulations;
The stack manipulation that is applied after the method invocation.
/** * The stack manipulation that is applied after the method invocation. */
private final StackManipulation terminatingStackManipulation;
Creates a new method binding.
Params:
  • target – The target method this binding represents.
  • registeredTargetIndices – A map of identification tokens to the indices of their binding parameters.
  • methodInvocation – A stack manipulation that represents the actual method invocation.
  • parameterStackManipulations – A list of manipulations that each represent the loading of a parameter value onto the operand stack.
  • terminatingStackManipulation – The stack manipulation that is applied after the method invocation.
/** * Creates a new method binding. * * @param target The target method this binding represents. * @param registeredTargetIndices A map of identification tokens to the indices of their binding * parameters. * @param methodInvocation A stack manipulation that represents the actual method invocation. * @param parameterStackManipulations A list of manipulations that each represent the loading of a * parameter value onto the operand stack. * @param terminatingStackManipulation The stack manipulation that is applied after the method invocation. */
protected Build(MethodDescription target, Map<?, Integer> registeredTargetIndices, StackManipulation methodInvocation, List<StackManipulation> parameterStackManipulations, StackManipulation terminatingStackManipulation) { this.target = target; this.registeredTargetIndices = new HashMap<Object, Integer>(registeredTargetIndices); this.methodInvocation = methodInvocation; this.parameterStackManipulations = new ArrayList<StackManipulation>(parameterStackManipulations); this.terminatingStackManipulation = terminatingStackManipulation; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { boolean result = methodInvocation.isValid() && terminatingStackManipulation.isValid(); Iterator<StackManipulation> assignment = parameterStackManipulations.iterator(); while (result && assignment.hasNext()) { result = assignment.next().isValid(); } return result; }
{@inheritDoc}
/** * {@inheritDoc} */
public Integer getTargetParameterIndex(Object parameterBindingToken) { return registeredTargetIndices.get(parameterBindingToken); }
{@inheritDoc}
/** * {@inheritDoc} */
public MethodDescription getTarget() { return target; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { return new Compound( CompoundList.of(parameterStackManipulations, Arrays.asList(methodInvocation, terminatingStackManipulation)) ).apply(methodVisitor, implementationContext); } } } }
A binding resolver is responsible to choose a method binding between several possible candidates.
/** * A binding resolver is responsible to choose a method binding between several possible candidates. */
interface BindingResolver {
Resolves a method binding for the source method.
Params:
  • ambiguityResolver – The ambiguity resolver to use.
  • source – The source method being bound.
  • targets – The possible target candidates. The list contains at least one element.
Returns:The method binding that was chosen.
/** * Resolves a method binding for the {@code source} method. * * @param ambiguityResolver The ambiguity resolver to use. * @param source The source method being bound. * @param targets The possible target candidates. The list contains at least one element. * @return The method binding that was chosen. */
MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets);
A default implementation of a binding resolver that fully relies on an AmbiguityResolver.
/** * A default implementation of a binding resolver that fully relies on an {@link AmbiguityResolver}. */
enum Default implements BindingResolver {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
Represents the index of the only value of two elements in a list.
/** * Represents the index of the only value of two elements in a list. */
private static final int ONLY = 0;
Represents the index of the left value of two elements in a list.
/** * Represents the index of the left value of two elements in a list. */
private static final int LEFT = 0;
Represents the index of the right value of two elements in a list.
/** * Represents the index of the right value of two elements in a list. */
private static final int RIGHT = 1;
{@inheritDoc}
/** * {@inheritDoc} */
public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) { return doResolve(ambiguityResolver, source, new ArrayList<MethodBinding>(targets)); }
Resolves a method binding for the source method.
Params:
  • ambiguityResolver – The ambiguity resolver to use.
  • source – The source method being bound.
  • targets – The possible target candidates. The list contains at least one element and is mutable.
Returns:The method binding that was chosen.
/** * Resolves a method binding for the {@code source} method. * * @param ambiguityResolver The ambiguity resolver to use. * @param source The source method being bound. * @param targets The possible target candidates. The list contains at least one element and is mutable. * @return The method binding that was chosen. */
private MethodBinding doResolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) { switch (targets.size()) { case 1: return targets.get(ONLY); case 2: { MethodBinding left = targets.get(LEFT); MethodBinding right = targets.get(RIGHT); switch (ambiguityResolver.resolve(source, left, right)) { case LEFT: return left; case RIGHT: return right; case AMBIGUOUS: case UNKNOWN: throw new IllegalArgumentException("Cannot resolve ambiguous delegation of " + source + " to " + left.getTarget() + " or " + right.getTarget()); default: throw new AssertionError(); } } default: /* case 3+: */ { MethodBinding left = targets.get(LEFT); MethodBinding right = targets.get(RIGHT); switch (ambiguityResolver.resolve(source, left, right)) { case LEFT: targets.remove(RIGHT); return doResolve(ambiguityResolver, source, targets); case RIGHT: targets.remove(LEFT); return doResolve(ambiguityResolver, source, targets); case AMBIGUOUS: case UNKNOWN: targets.remove(RIGHT); // Remove right element first due to index alteration! targets.remove(LEFT); MethodBinding subResult = doResolve(ambiguityResolver, source, targets); switch (ambiguityResolver.resolve(source, left, subResult).merge(ambiguityResolver.resolve(source, right, subResult))) { case RIGHT: return subResult; case LEFT: case AMBIGUOUS: case UNKNOWN: throw new IllegalArgumentException("Cannot resolve ambiguous delegation of " + source + " to " + left.getTarget() + " or " + right.getTarget()); default: throw new AssertionError(); } default: throw new IllegalStateException("Unexpected amount of targets: " + targets.size()); } } } } }
A binding resolver that only binds a method if it has a unique binding.
/** * A binding resolver that only binds a method if it has a unique binding. */
enum Unique implements BindingResolver {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
Indicates the first index of a list only containing one element.
/** * Indicates the first index of a list only containing one element. */
private static final int ONLY = 0;
{@inheritDoc}
/** * {@inheritDoc} */
public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) { if (targets.size() == 1) { return targets.get(ONLY); } else { throw new IllegalStateException(source + " allowed for more than one binding: " + targets); } } }
Binds a method using another resolver and prints the selected binding to a PrintStream.
/** * Binds a method using another resolver and prints the selected binding to a {@link PrintStream}. */
@HashCodeAndEqualsPlugin.Enhance class StreamWriting implements BindingResolver {
The delegate binding resolver.
/** * The delegate binding resolver. */
private final BindingResolver delegate;
The print stream to bind write the chosen binding to.
/** * The print stream to bind write the chosen binding to. */
private final PrintStream printStream;
Creates a new stream writing binding resolver.
Params:
  • delegate – The delegate binding resolver.
  • printStream – The print stream to bind write the chosen binding to.
/** * Creates a new stream writing binding resolver. * * @param delegate The delegate binding resolver. * @param printStream The print stream to bind write the chosen binding to. */
public StreamWriting(BindingResolver delegate, PrintStream printStream) { this.delegate = delegate; this.printStream = printStream; }
Creates a binding resolver that writes results to System.out and delegates to the Default resolver.
Returns:An appropriate binding resolver.
/** * Creates a binding resolver that writes results to {@link System#out} and delegates to the {@link Default} resolver. * * @return An appropriate binding resolver. */
public static BindingResolver toSystemOut() { return toSystemOut(Default.INSTANCE); }
Creates a binding resolver that writes results to System.out and delegates to the Default resolver.
Params:
  • bindingResolver – The delegate binding resolver.
Returns:An appropriate binding resolver.
/** * Creates a binding resolver that writes results to {@link System#out} and delegates to the {@link Default} resolver. * * @param bindingResolver The delegate binding resolver. * @return An appropriate binding resolver. */
public static BindingResolver toSystemOut(BindingResolver bindingResolver) { return new StreamWriting(bindingResolver, System.out); }
Creates a binding resolver that writes results to System.err and delegates to the Default resolver.
Returns:An appropriate binding resolver.
/** * Creates a binding resolver that writes results to {@link System#err} and delegates to the {@link Default} resolver. * * @return An appropriate binding resolver. */
public static BindingResolver toSystemError() { return toSystemError(Default.INSTANCE); }
Creates a binding resolver that writes results to System.err.
Params:
  • bindingResolver – The delegate binding resolver.
Returns:An appropriate binding resolver.
/** * Creates a binding resolver that writes results to {@link System#err}. * * @param bindingResolver The delegate binding resolver. * @return An appropriate binding resolver. */
public static BindingResolver toSystemError(BindingResolver bindingResolver) { return new StreamWriting(bindingResolver, System.err); }
{@inheritDoc}
/** * {@inheritDoc} */
public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) { MethodBinding methodBinding = delegate.resolve(ambiguityResolver, source, targets); printStream.println("Binding " + source + " as delegation to " + methodBinding.getTarget()); return methodBinding; } } }
Implementations of this interface are able to attempt the resolution of two successful bindings of a method to two different target methods in order to identify a dominating binding.
/** * Implementations of this interface are able to attempt the resolution of two successful bindings of a method * to two different target methods in order to identify a dominating binding. */
@SuppressFBWarnings(value = "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION", justification = "Safe initialization is implied") interface AmbiguityResolver {
The default ambiguity resolver to use.
/** * The default ambiguity resolver to use. */
AmbiguityResolver DEFAULT = new MethodDelegationBinder.AmbiguityResolver.Compound(BindingPriority.Resolver.INSTANCE, DeclaringTypeResolver.INSTANCE, ArgumentTypeResolver.INSTANCE, MethodNameEqualityResolver.INSTANCE, ParameterLengthResolver.INSTANCE);
Attempts to resolve to conflicting bindings.
Params:
  • source – The source method that was bound to both target methods.
  • left – The first successful binding of the source method.
  • right – The second successful binding of the source method.
Returns:The resolution state when resolving a conflicting binding where Resolution.LEFT indicates a successful binding to the left binding while Resolution.RIGHT indicates a successful binding to the right binding.
/** * Attempts to resolve to conflicting bindings. * * @param source The source method that was bound to both target methods. * @param left The first successful binding of the {@code source} method. * @param right The second successful binding of the {@code source} method. * @return The resolution state when resolving a conflicting binding where * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#LEFT} * indicates a successful binding to the {@code left} binding while * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#RIGHT} * indicates a successful binding to the {@code right} binding. */
Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right);
A resolution state of an attempt to resolve two conflicting bindings.
/** * A resolution state of an attempt to resolve two conflicting bindings. */
enum Resolution {
Describes a resolution state where no information about dominance could be gathered.
/** * Describes a resolution state where no information about dominance could be gathered. */
UNKNOWN(true),
Describes a resolution state where the left method dominates the right method.
/** * Describes a resolution state where the left method dominates the right method. */
LEFT(false),
Describes a resolution state where the right method dominates the left method.
/** * Describes a resolution state where the right method dominates the left method. */
RIGHT(false),
Describes a resolution state where both methods have inflicting dominance over each other.
/** * Describes a resolution state where both methods have inflicting dominance over each other. */
AMBIGUOUS(true);
true if this resolution is unresolved.
/** * {@code true} if this resolution is unresolved. */
private final boolean unresolved;
Creates a new resolution.
Params:
  • unresolved – true if this resolution is unresolved.
/** * Creates a new resolution. * * @param unresolved {@code true} if this resolution is unresolved. */
Resolution(boolean unresolved) { this.unresolved = unresolved; }
Checks if this binding is unresolved.
Returns:true if this binding is unresolved.
/** * Checks if this binding is unresolved. * * @return {@code true} if this binding is unresolved. */
public boolean isUnresolved() { return unresolved; }
Merges two resolutions in order to determine their compatibility.
Params:
  • other – The resolution this resolution is to be checked against.
Returns:The merged resolution.
/** * Merges two resolutions in order to determine their compatibility. * * @param other The resolution this resolution is to be checked against. * @return The merged resolution. */
public Resolution merge(Resolution other) { switch (this) { case UNKNOWN: return other; case AMBIGUOUS: return AMBIGUOUS; case LEFT: case RIGHT: return other == UNKNOWN || other == this ? this : AMBIGUOUS; default: throw new AssertionError(); } } }
An ambiguity resolver that does not attempt to resolve a conflicting binding.
/** * An ambiguity resolver that does not attempt to resolve a conflicting binding. */
enum NoOp implements AmbiguityResolver {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) { return Resolution.UNKNOWN; } }
An ambiguity resolver that always resolves in the specified direction.
/** * An ambiguity resolver that always resolves in the specified direction. */
enum Directional implements AmbiguityResolver {
A resolver that always resolves to Resolution.LEFT.
/** * A resolver that always resolves to * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#LEFT}. */
LEFT(true),
A resolver that always resolves to Resolution.RIGHT.
/** * A resolver that always resolves to * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#RIGHT}. */
RIGHT(false);
true if this instance should resolve to the left side.
/** * {@code true} if this instance should resolve to the left side. */
private final boolean left;
Creates a new directional resolver.
Params:
  • left – true if this instance should resolve to the left side.
/** * Creates a new directional resolver. * * @param left {@code true} if this instance should resolve to the left side. */
Directional(boolean left) { this.left = left; }
{@inheritDoc}
/** * {@inheritDoc} */
public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) { return this.left ? Resolution.LEFT : Resolution.RIGHT; } }
A chain of AmbiguityResolvers that are applied in the given order until two bindings can be resolved.
/** * A chain of {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}s * that are applied in the given order until two bindings can be resolved. */
@HashCodeAndEqualsPlugin.Enhance class Compound implements AmbiguityResolver {
A list of ambiguity resolvers that are applied by this chain in their order of application.
/** * A list of ambiguity resolvers that are applied by this chain in their order of application. */
private final List<AmbiguityResolver> ambiguityResolvers;
Creates an immutable chain of ambiguity resolvers.
Params:
  • ambiguityResolver – The ambiguity resolvers to chain in the order of their application.
/** * Creates an immutable chain of ambiguity resolvers. * * @param ambiguityResolver The ambiguity resolvers to chain in the order of their application. */
public Compound(AmbiguityResolver... ambiguityResolver) { this(Arrays.asList(ambiguityResolver)); }
Creates an immutable chain of ambiguity resolvers.
Params:
  • ambiguityResolvers – The ambiguity resolvers to chain in the order of their application.
/** * Creates an immutable chain of ambiguity resolvers. * * @param ambiguityResolvers The ambiguity resolvers to chain in the order of their application. */
public Compound(List<? extends AmbiguityResolver> ambiguityResolvers) { this.ambiguityResolvers = new ArrayList<AmbiguityResolver>(); for (AmbiguityResolver ambiguityResolver : ambiguityResolvers) { if (ambiguityResolver instanceof Compound) { this.ambiguityResolvers.addAll(((Compound) ambiguityResolver).ambiguityResolvers); } else if (!(ambiguityResolver instanceof NoOp)) { this.ambiguityResolvers.add(ambiguityResolver); } } }
{@inheritDoc}
/** * {@inheritDoc} */
public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) { Resolution resolution = Resolution.UNKNOWN; Iterator<? extends AmbiguityResolver> iterator = ambiguityResolvers.iterator(); while (resolution.isUnresolved() && iterator.hasNext()) { resolution = iterator.next().resolve(source, left, right); } return resolution; } } }
A termination handler is responsible for terminating a method delegation.
/** * A termination handler is responsible for terminating a method delegation. */
interface TerminationHandler {
Creates a stack manipulation that is to be applied after the method return.
Params:
  • assigner – The supplied assigner.
  • typing – The typing to apply.
  • source – The source method that is bound to the target method.
  • target – The target method that is subject to be bound by the source method.
Returns:A stack manipulation that is applied after the method return.
/** * Creates a stack manipulation that is to be applied after the method return. * * @param assigner The supplied assigner. * @param typing The typing to apply. * @param source The source method that is bound to the {@code target} method. * @param target The target method that is subject to be bound by the {@code source} method. * @return A stack manipulation that is applied after the method return. */
StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target);
Responsible for creating a StackManipulation that is applied after the interception method is applied.
/** * Responsible for creating a {@link StackManipulation} * that is applied after the interception method is applied. */
enum Default implements TerminationHandler {
A termination handler that returns the delegate method's return value.
/** * A termination handler that returns the delegate method's return value. */
RETURNING {
{@inheritDoc}
/** {@inheritDoc} */
public StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target) { return new StackManipulation.Compound(assigner.assign(target.isConstructor() ? target.getDeclaringType().asGenericType() : target.getReturnType(), source.getReturnType(), typing), MethodReturn.of(source.getReturnType())); } },
A termination handler that drops the delegate method's return value.
/** * A termination handler that drops the delegate method's return value. */
DROPPING {
{@inheritDoc}
/** {@inheritDoc} */
public StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target) { return Removal.of(target.isConstructor() ? target.getDeclaringType() : target.getReturnType()); } }; } }
A helper class that allows to identify a best binding for a given type and source method choosing from a list of given target methods by using a given MethodDelegationBinder and an AmbiguityResolver.

 

The Processor will:
  1. Try to bind the source method using the MethodDelegationBinder.
  2. Find a best method among the successful bindings using the AmbiguityResolver.
/** * A helper class that allows to identify a best binding for a given type and source method choosing from a list of given * target methods by using a given {@link net.bytebuddy.implementation.bind.MethodDelegationBinder} * and an {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}. * <p>&nbsp;</p> * The {@code Processor} will: * <ol> * <li>Try to bind the {@code source} method using the {@code MethodDelegationBinder}.</li> * <li>Find a best method among the successful bindings using the {@code AmbiguityResolver}.</li> * </ol> */
@HashCodeAndEqualsPlugin.Enhance class Processor implements MethodDelegationBinder.Record {
The delegation records to consider.
/** * The delegation records to consider. */
private final List<? extends Record> records;
The processor's ambiguity resolver.
/** * The processor's ambiguity resolver. */
private final AmbiguityResolver ambiguityResolver;
The binding resolver being used to select the relevant method binding.
/** * The binding resolver being used to select the relevant method binding. */
private final BindingResolver bindingResolver;
Creates a new processor.
Params:
  • records – The delegation records to consider.
  • ambiguityResolver – The ambiguity resolver to apply.
  • bindingResolver – The binding resolver being used to select the relevant method binding.
/** * Creates a new processor. * * @param records The delegation records to consider. * @param ambiguityResolver The ambiguity resolver to apply. * @param bindingResolver The binding resolver being used to select the relevant method binding. */
public Processor(List<? extends Record> records, AmbiguityResolver ambiguityResolver, BindingResolver bindingResolver) { this.records = records; this.ambiguityResolver = ambiguityResolver; this.bindingResolver = bindingResolver; }
{@inheritDoc}
/** * {@inheritDoc} */
public MethodBinding bind(Implementation.Target implementationTarget, MethodDescription source, TerminationHandler terminationHandler, MethodInvoker methodInvoker, Assigner assigner) { List<MethodBinding> targets = new ArrayList<MethodBinding>(); for (Record record : records) { MethodBinding methodBinding = record.bind(implementationTarget, source, terminationHandler, methodInvoker, assigner); if (methodBinding.isValid()) { targets.add(methodBinding); } } if (targets.isEmpty()) { throw new IllegalArgumentException("None of " + records + " allows for delegation from " + source); } return bindingResolver.resolve(ambiguityResolver, source, targets); } } }