Skip to content

Commit

Permalink
Refactored method delegation processor.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Jul 25, 2015
1 parent ebe6236 commit 1d80a6e
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 92 deletions.
Expand Up @@ -177,7 +177,7 @@ public class MethodDelegation implements Implementation {
/**
* A list of methods to be considered as target by this method delegation.
*/
private final MethodList targetMethodCandidates;
private final MethodList targetCandidates;

/**
* Creates a new method delegation.
Expand All @@ -188,7 +188,7 @@ public class MethodDelegation implements Implementation {
* @param terminationHandler The termination handler to apply.
* @param ambiguityResolver The ambiguity resolver to use by this method delegator.
* @param assigner The assigner to be supplied by this method delegator.
* @param targetMethodCandidates A list of methods that should be considered as possible binding targets by
* @param targetCandidates A list of methods that should be considered as possible binding targets by
* this method delegator.
*/
protected MethodDelegation(ImplementationDelegate implementationDelegate,
Expand All @@ -197,14 +197,14 @@ protected MethodDelegation(ImplementationDelegate implementationDelegate,
TargetMethodAnnotationDrivenBinder.TerminationHandler terminationHandler,
MethodDelegationBinder.AmbiguityResolver ambiguityResolver,
Assigner assigner,
MethodList targetMethodCandidates) {
MethodList targetCandidates) {
this.implementationDelegate = implementationDelegate;
this.parameterBinders = parameterBinders;
this.defaultsProvider = defaultsProvider;
this.terminationHandler = terminationHandler;
this.ambiguityResolver = ambiguityResolver;
this.assigner = assigner;
this.targetMethodCandidates = isNotEmpty(targetMethodCandidates, NO_METHODS_ERROR_MESSAGE);
this.targetCandidates = isNotEmpty(targetCandidates, NO_METHODS_ERROR_MESSAGE);
}

/**
Expand Down Expand Up @@ -489,7 +489,7 @@ public MethodDelegation appendParameterBinder(TargetMethodAnnotationDrivenBinder
terminationHandler,
ambiguityResolver,
assigner,
targetMethodCandidates);
targetCandidates);
}

/**
Expand All @@ -505,7 +505,7 @@ public MethodDelegation defineParameterBinder(TargetMethodAnnotationDrivenBinder
terminationHandler,
ambiguityResolver,
assigner,
targetMethodCandidates);
targetCandidates);
}

/**
Expand All @@ -521,7 +521,7 @@ public MethodDelegation withDefaultsProvider(TargetMethodAnnotationDrivenBinder.
terminationHandler,
ambiguityResolver,
assigner,
targetMethodCandidates);
targetCandidates);
}

/**
Expand All @@ -548,7 +548,7 @@ public MethodDelegation defineAmbiguityResolver(MethodDelegationBinder.Ambiguity
terminationHandler,
MethodDelegationBinder.AmbiguityResolver.Chain.of(nonNull(ambiguityResolver)),
assigner,
targetMethodCandidates);
targetCandidates);
}

/**
Expand All @@ -564,7 +564,7 @@ public MethodDelegation withAssigner(Assigner assigner) {
terminationHandler,
ambiguityResolver,
nonNull(assigner),
targetMethodCandidates);
targetCandidates);
}

/**
Expand All @@ -580,7 +580,7 @@ public MethodDelegation filter(ElementMatcher<? super MethodDescription> methodM
terminationHandler,
ambiguityResolver,
assigner,
isNotEmpty(targetMethodCandidates.filter(nonNull(methodMatcher)), NO_METHODS_ERROR_MESSAGE));
isNotEmpty(targetCandidates.filter(nonNull(methodMatcher)), NO_METHODS_ERROR_MESSAGE));
}

/**
Expand All @@ -600,7 +600,7 @@ public Implementation andThen(Implementation implementation) {
TargetMethodAnnotationDrivenBinder.TerminationHandler.Dropping.INSTANCE,
ambiguityResolver,
assigner,
targetMethodCandidates), nonNull(implementation));
targetCandidates), nonNull(implementation));
}

@Override
Expand All @@ -610,15 +610,10 @@ public InstrumentedType prepare(InstrumentedType instrumentedType) {

@Override
public ByteCodeAppender appender(Target implementationTarget) {
MethodList methodList = this.targetMethodCandidates.filter(isVisibleTo(implementationTarget.getTypeDescription()));
if (methodList.size() == 0) {
throw new IllegalStateException("No bindable method is visible to " + implementationTarget.getTypeDescription());
}
return new Appender(implementationDelegate.getPreparingStackAssignment(implementationTarget.getTypeDescription()),
implementationTarget,
methodList,
new MethodDelegationBinder.Processor(new TargetMethodAnnotationDrivenBinder(
parameterBinders,
targetCandidates,
new MethodDelegationBinder.Processor(new TargetMethodAnnotationDrivenBinder(parameterBinders,
defaultsProvider,
terminationHandler,
assigner,
Expand All @@ -637,7 +632,7 @@ public boolean equals(Object other) {
&& defaultsProvider.equals(that.defaultsProvider)
&& terminationHandler.equals(that.terminationHandler)
&& implementationDelegate.equals(that.implementationDelegate)
&& targetMethodCandidates.equals(that.targetMethodCandidates)
&& targetCandidates.equals(that.targetCandidates)
&& parameterBinders.equals(that.parameterBinders);
}

Expand All @@ -649,7 +644,7 @@ public int hashCode() {
result = 31 * result + terminationHandler.hashCode();
result = 31 * result + ambiguityResolver.hashCode();
result = 31 * result + assigner.hashCode();
result = 31 * result + targetMethodCandidates.hashCode();
result = 31 * result + targetCandidates.hashCode();
return result;
}

Expand All @@ -662,7 +657,7 @@ public String toString() {
", terminationHandler=" + terminationHandler +
", ambiguityResolver=" + ambiguityResolver +
", assigner=" + assigner +
", targetMethodCandidates=" + targetMethodCandidates +
", targetCandidates=" + targetCandidates +
'}';
}

Expand Down Expand Up @@ -731,6 +726,11 @@ public String toString() {
*/
class ForStaticField implements ImplementationDelegate {

/**
* The modifier to be assigned to a static field interceptor.
*/
private static final int FIELD_MODIFIERS = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;

/**
* The name prefix for the {@code static} field that is containing the delegation target.
*/
Expand Down Expand Up @@ -769,16 +769,14 @@ public ForStaticField(Object delegate, String fieldName) {

@Override
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType.withField(fieldName,
new TypeDescription.ForLoadedType(delegate.getClass()),
Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC)
return instrumentedType
.withField(fieldName, new TypeDescription.ForLoadedType(delegate.getClass()), FIELD_MODIFIERS)
.withInitializer(LoadedTypeInitializer.ForStaticField.nonAccessible(fieldName, delegate));
}

@Override
public StackManipulation getPreparingStackAssignment(TypeDescription instrumentedType) {
return FieldAccess.forField(instrumentedType.getDeclaredFields()
.filter((named(fieldName))).getOnly()).getter();
return FieldAccess.forField(instrumentedType.getDeclaredFields().filter((named(fieldName))).getOnly()).getter();
}

@Override
Expand Down Expand Up @@ -841,9 +839,8 @@ public InstrumentedType prepare(InstrumentedType instrumentedType) {

@Override
public StackManipulation getPreparingStackAssignment(TypeDescription instrumentedType) {
return new StackManipulation.Compound(MethodVariableAccess.forType(instrumentedType).loadOffset(0),
FieldAccess.forField(instrumentedType.getDeclaredFields()
.filter((named(fieldName))).getOnly()).getter());
return new StackManipulation.Compound(MethodVariableAccess.REFERENCE.loadOffset(0),
FieldAccess.forField(instrumentedType.getDeclaredFields().filter((named(fieldName))).getOnly()).getter());
}

@Override
Expand Down Expand Up @@ -947,7 +944,7 @@ protected static class Appender implements ByteCodeAppender {
/**
* The method candidates to consider for delegating the invocation to.
*/
private final Iterable<? extends MethodDescription> targetMethods;
private final MethodList targetCandidates;

/**
* The method delegation binder processor which is responsible for implementing the method delegation.
Expand All @@ -960,27 +957,25 @@ protected static class Appender implements ByteCodeAppender {
* @param preparingStackAssignment The stack manipulation that is responsible for loading a potential target
* instance onto the stack on which the target method is invoked.
* @param implementationTarget The implementation target of this implementation.
* @param targetMethods The method candidates to consider for delegating the invocation to.
* @param targetCandidates The method candidates to consider for delegating the invocation to.
* @param processor The method delegation binder processor which is responsible for implementing
* the method delegation.
*/
protected Appender(StackManipulation preparingStackAssignment,
Target implementationTarget,
Iterable<? extends MethodDescription> targetMethods,
MethodList targetCandidates,
MethodDelegationBinder.Processor processor) {
this.preparingStackAssignment = preparingStackAssignment;
this.implementationTarget = implementationTarget;
this.targetMethods = targetMethods;
this.targetCandidates = targetCandidates;
this.processor = processor;
}

@Override
public Size apply(MethodVisitor methodVisitor,
Context implementationContext,
MethodDescription instrumentedMethod) {
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
StackManipulation.Size stackSize = new StackManipulation.Compound(
preparingStackAssignment,
processor.process(implementationTarget, instrumentedMethod, targetMethods)
processor.process(implementationTarget, instrumentedMethod, targetCandidates)
).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
Expand All @@ -993,14 +988,14 @@ public boolean equals(Object other) {
return implementationTarget.equals(that.implementationTarget)
&& preparingStackAssignment.equals(that.preparingStackAssignment)
&& processor.equals(that.processor)
&& targetMethods.equals(that.targetMethods);
&& targetCandidates.equals(that.targetCandidates);
}

@Override
public int hashCode() {
int result = preparingStackAssignment.hashCode();
result = 31 * result + implementationTarget.hashCode();
result = 31 * result + targetMethods.hashCode();
result = 31 * result + targetCandidates.hashCode();
result = 31 * result + processor.hashCode();
return result;
}
Expand All @@ -1010,7 +1005,7 @@ public String toString() {
return "MethodDelegation.Appender{" +
"preparingStackAssignment=" + preparingStackAssignment +
", implementationTarget=" + implementationTarget +
", targetMethods=" + targetMethods +
", targetCandidates=" + targetCandidates +
", processor=" + processor +
'}';
}
Expand Down
@@ -1,6 +1,7 @@
package net.bytebuddy.implementation.bind;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.StackManipulation;
Expand All @@ -9,6 +10,8 @@

import java.util.*;

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

/**
* 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.
Expand Down Expand Up @@ -834,66 +837,39 @@ class Processor {
* @param methodDelegationBinder This processor's method delegation binder.
* @param ambiguityResolver The processor's ambiguity resolver.
*/
public Processor(MethodDelegationBinder methodDelegationBinder,
AmbiguityResolver ambiguityResolver) {
public Processor(MethodDelegationBinder methodDelegationBinder, AmbiguityResolver ambiguityResolver) {
this.methodDelegationBinder = methodDelegationBinder;
this.ambiguityResolver = ambiguityResolver;
}

/**
* Returns the {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}
* used by this {@code Processor}.
*
* @return The method delegation binder used by this {@code Processor}.
*/
public MethodDelegationBinder getMethodDelegationBinder() {
return methodDelegationBinder;
}

/**
* Returns the {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}
* used by this {@code Processor}.
*
* @return The ambiguity resolver used by this {@code Processor}.
*/
public AmbiguityResolver getAmbiguityResolver() {
return ambiguityResolver;
}

/**
* @param implementationTarget The implementation target for binding the {@code source} method to.
* @param source The source method that is to be bound.
* @param targets All possible targets for the delegation binding that are to be considered.
* @param implementationTarget The implementation target for binding the {@code source} method to.
* @param source The source method that is to be bound.
* @param targetCandidates All possible targets for the delegation binding that are to be considered.
* @return The best binding that was identified. If no such binding can be identified, an exception is thrown.
*/
public MethodBinding process(Implementation.Target implementationTarget,
MethodDescription source,
Iterable<? extends MethodDescription> targets) {
List<MethodBinding> possibleDelegations = bind(implementationTarget, source, targets);
if (possibleDelegations.size() == 0) {
throw new IllegalArgumentException("No method can be bound to " + source);
public MethodBinding process(Implementation.Target implementationTarget, MethodDescription source, MethodList targetCandidates) {
List<MethodBinding> possibleDelegations = bind(implementationTarget, source, targetCandidates);
if (possibleDelegations.isEmpty()) {
throw new IllegalArgumentException("No delegator method can be bound to " + source);
}
return resolve(source, possibleDelegations);
}

/**
* Creates a list of method bindings for any legal target method.
*
* @param implementationTarget The implementation target for binding the {@code source} method to.
* @param source The method that is to be bound to any {@code targets} method.
* @param targets All possible targets for the delegation binding that are to be considered.
* @param implementationTarget The implementation target for binding the {@code source} method to.
* @param source The method that is to be bound to any {@code targets} method.
* @param targetCandidates All possible targets for the delegation binding that are to be considered.
* @return A list of valid method bindings representing a subset of the given target methods.
*/
private List<MethodBinding> bind(Implementation.Target implementationTarget,
MethodDescription source,
Iterable<? extends MethodDescription> targets) {
private List<MethodBinding> bind(Implementation.Target implementationTarget, MethodDescription source, MethodList targetCandidates) {
List<MethodBinding> possibleDelegations = new LinkedList<MethodBinding>();
for (MethodDescription target : targets) {
if (target.isVisibleTo(implementationTarget.getTypeDescription())) {
MethodBinding methodBinding = methodDelegationBinder.bind(implementationTarget, source, target);
if (methodBinding.isValid()) {
possibleDelegations.add(methodBinding);
}
for (MethodDescription targetCandidate : targetCandidates.filter(isVisibleTo(implementationTarget.getTypeDescription()))) {
MethodBinding methodBinding = methodDelegationBinder.bind(implementationTarget, source, targetCandidate);
if (methodBinding.isValid()) {
possibleDelegations.add(methodBinding);
}
}
return possibleDelegations;
Expand Down
@@ -1,9 +1,12 @@
package net.bytebuddy.implementation;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.bind.annotation.Argument;
import org.junit.Test;

import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

Expand Down Expand Up @@ -37,4 +40,31 @@ public static String baz(String s, Object o) {
return BAZ + s + o;
}
}

@Test
public void testHierarchyDelegation() throws Exception {
new ByteBuddy()
.subclass(Baz.class)
.method(named("foo"))
.intercept(MethodDelegation.to(new Qux()))
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded()
.newInstance();
}

public static class Baz {

public void foo() {

}
}

public static class Qux extends Baz {

@Override
public void foo() {
super.foo();
}
}
}

0 comments on commit 1d80a6e

Please sign in to comment.