1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.implementation.bind;
17
18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.implementation.Implementation;
23 import net.bytebuddy.implementation.bind.annotation.BindingPriority;
24 import net.bytebuddy.implementation.bytecode.Removal;
25 import net.bytebuddy.implementation.bytecode.StackManipulation;
26 import net.bytebuddy.implementation.bytecode.assign.Assigner;
27 import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
28 import net.bytebuddy.implementation.bytecode.member.MethodReturn;
29 import net.bytebuddy.utility.CompoundList;
30 import net.bytebuddy.jar.asm.MethodVisitor;
31
32 import java.io.PrintStream;
33 import java.util.*;
34
35 /**
36 * A method delegation binder is responsible for creating a method binding for a <i>source method</i> to a
37 * <i>target method</i>. Such a binding allows to implement the source method by calling the target method.
38 * <p> </p>
39 * Usually, an implementation will attempt to bind a specific source method to a set of target method candidates
40 * where all legal bindings are considered for binding. To chose a specific candidate, an
41 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}
42 * will be consulted for selecting a <i>best</i> binding.
43 */
44 public interface MethodDelegationBinder {
45
46 /**
47 * Compiles this method delegation binder for a target method.
48 *
49 * @param candidate The target method to bind.
50 * @return A compiled target for binding.
51 */
52 Record compile(MethodDescription candidate);
53
54 /**
55 * A method delegation that was compiled to a target method.
56 */
57 interface Record {
58
59 /**
60 * Attempts a binding of a source method to this compiled target.
61 *
62 * @param implementationTarget The target of the current implementation onto which this binding is to be applied.
63 * @param source The method that is to be bound to the {@code target} method.
64 * @param terminationHandler The termination handler to apply.
65 * @param methodInvoker The method invoker to use.
66 * @param assigner The assigner to use.
67 * @return A binding representing this attempt to bind the {@code source} method to the {@code target} method.
68 */
69 MethodBinding bind(Implementation.Target implementationTarget,
70 MethodDescription source,
71 TerminationHandler terminationHandler,
72 MethodInvoker methodInvoker,
73 Assigner assigner);
74
75 /**
76 * A compiled method delegation binder that only yields illegal bindings.
77 */
78 enum Illegal implements Record {
79
80 /**
81 * The singleton instance.
82 */
83 INSTANCE;
84
85 /**
86 * {@inheritDoc}
87 */
88 public MethodBinding bind(Implementation.Target implementationTarget,
89 MethodDescription source,
90 TerminationHandler terminationHandler,
91 MethodInvoker methodInvoker,
92 Assigner assigner) {
93 return MethodBinding.Illegal.INSTANCE;
94 }
95 }
96 }
97
98 /**
99 * Implementations are used as delegates for invoking a method that was bound
100 * using a {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}.
101 */
102 interface MethodInvoker {
103
104 /**
105 * Creates a method invocation for a given method.
106 *
107 * @param methodDescription The method to be invoked.
108 * @return A stack manipulation encapsulating this method invocation.
109 */
110 StackManipulation invoke(MethodDescription methodDescription);
111
112 /**
113 * A simple method invocation that merely uses the most general form of method invocation as provided by
114 * {@link net.bytebuddy.implementation.bytecode.member.MethodInvocation}.
115 */
116 enum Simple implements MethodInvoker {
117
118 /**
119 * The singleton instance.
120 */
121 INSTANCE;
122
123 /**
124 * {@inheritDoc}
125 */
126 public StackManipulation invoke(MethodDescription methodDescription) {
127 return MethodInvocation.invoke(methodDescription);
128 }
129 }
130
131 /**
132 * A method invocation that enforces a virtual invocation that is dispatched on a given type.
133 */
134 @HashCodeAndEqualsPlugin.Enhance
135 class Virtual implements MethodInvoker {
136
137 /**
138 * The type on which a method should be invoked virtually.
139 */
140 private final TypeDescription typeDescription;
141
142 /**
143 * Creates an immutable method invoker that dispatches all methods on a given type.
144 *
145 * @param typeDescription The type on which the method is invoked by virtual invocation.
146 */
147 public Virtual(TypeDescription typeDescription) {
148 this.typeDescription = typeDescription;
149 }
150
151 /**
152 * {@inheritDoc}
153 */
154 public StackManipulation invoke(MethodDescription methodDescription) {
155 return MethodInvocation.invoke(methodDescription).virtual(typeDescription);
156 }
157 }
158 }
159
160 /**
161 * A binding attempt for a single parameter. Implementations of this type are a suggestion of composing a
162 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding}
163 * by using a
164 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding.Builder}.
165 * However, method bindings can also be composed without this type which is merely a suggestion.
166 *
167 * @param <T> The type of the identification token for this parameter binding.
168 */
169 interface ParameterBinding<T> extends StackManipulation {
170
171 /**
172 * Returns an identification token for this binding.
173 *
174 * @return An identification token unique to this binding.
175 */
176 T getIdentificationToken();
177
178 /**
179 * A singleton representation of an illegal binding for a method parameter. An illegal binding usually
180 * suggests that a source method cannot be bound to a specific target method.
181 */
182 enum Illegal implements ParameterBinding<Void> {
183
184 /**
185 * The singleton instance.
186 */
187 INSTANCE;
188
189 /**
190 * {@inheritDoc}
191 */
192 public Void getIdentificationToken() {
193 throw new IllegalStateException("An illegal binding does not define an identification token");
194 }
195
196 /**
197 * {@inheritDoc}
198 */
199 public boolean isValid() {
200 return false;
201 }
202
203 /**
204 * {@inheritDoc}
205 */
206 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
207 throw new IllegalStateException("An illegal parameter binding must not be applied");
208 }
209 }
210
211 /**
212 * An anonymous binding of a target method parameter.
213 */
214 @HashCodeAndEqualsPlugin.Enhance
215 class Anonymous implements ParameterBinding<Object> {
216
217 /**
218 * A pseudo-token that is not exposed and therefore anonymous.
219 */
220 @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.IGNORE)
221 private final Object anonymousToken;
222
223 /**
224 * The stack manipulation that represents the loading of the parameter binding onto the stack.
225 */
226 private final StackManipulation delegate;
227
228 /**
229 * Creates a new, anonymous parameter binding.
230 *
231 * @param delegate The stack manipulation that is responsible for loading the parameter value for this
232 * target method parameter onto the stack.
233 */
234 public Anonymous(StackManipulation delegate) {
235 this.delegate = delegate;
236 anonymousToken = new Object();
237 }
238
239 /**
240 * {@inheritDoc}
241 */
242 public Object getIdentificationToken() {
243 return anonymousToken;
244 }
245
246 /**
247 * {@inheritDoc}
248 */
249 public boolean isValid() {
250 return delegate.isValid();
251 }
252
253 /**
254 * {@inheritDoc}
255 */
256 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
257 return delegate.apply(methodVisitor, implementationContext);
258 }
259 }
260
261 /**
262 * A uniquely identifiable parameter binding for a target method. Such bindings are usually later processed by
263 * a {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}
264 * in order to resolve binding conflicts between several bindable target methods to the same source method.
265 *
266 * @param <T> The type of the identification token.
267 * @see net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver
268 */
269 @HashCodeAndEqualsPlugin.Enhance
270 class Unique<T> implements ParameterBinding<T> {
271
272 /**
273 * The token that identifies this parameter binding as unique.
274 */
275 private final T identificationToken;
276
277 /**
278 * The stack manipulation that represents the loading of the parameter binding onto the stack.
279 */
280 private final StackManipulation delegate;
281
282 /**
283 * Creates a new unique parameter binding representant.
284 *
285 * @param delegate The stack manipulation that loads the argument for this parameter onto the operand stack.
286 * @param identificationToken The token used for identifying this parameter binding.
287 */
288 public Unique(StackManipulation delegate, T identificationToken) {
289 this.delegate = delegate;
290 this.identificationToken = identificationToken;
291 }
292
293 /**
294 * A factory method for creating a unique binding that infers the tokens type.
295 *
296 * @param delegate The stack manipulation delegate.
297 * @param identificationToken The identification token.
298 * @param <S> The type of the identification token.
299 * @return A new instance representing this unique binding.
300 */
301 public static <S> Unique<S> of(StackManipulation delegate, S identificationToken) {
302 return new Unique<S>(delegate, identificationToken);
303 }
304
305 /**
306 * {@inheritDoc}
307 */
308 public T getIdentificationToken() {
309 return identificationToken;
310 }
311
312 /**
313 * {@inheritDoc}
314 */
315 public boolean isValid() {
316 return delegate.isValid();
317 }
318
319 /**
320 * {@inheritDoc}
321 */
322 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
323 return delegate.apply(methodVisitor, implementationContext);
324 }
325 }
326 }
327
328 /**
329 * A binding attempt created by a
330 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}.
331 */
332 interface MethodBinding extends StackManipulation {
333
334 /**
335 * Returns the target method's parameter index for a given parameter binding token.
336 * <p> </p>
337 * A binding token can be any object
338 * that implements valid {@link Object#hashCode()} and {@link Object#equals(Object)} methods in order
339 * to look up a given binding. This way, two bindings can be evaluated of having performed a similar type of
340 * binding such that these bindings can be compared and a dominant binding can be identified by an
341 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}.
342 * Furthermore, a binding is implicitly required to insure the uniqueness of such a parameter binding.
343 *
344 * @param parameterBindingToken A token which is used to identify a specific unique binding for a given parameter
345 * of the target method.
346 * @return The target method's parameter index of this binding or {@code null} if no such argument binding
347 * was applied for this binding.
348 */
349 Integer getTargetParameterIndex(Object parameterBindingToken);
350
351 /**
352 * Returns the target method of the method binding attempt.
353 *
354 * @return The target method to which the
355 */
356 MethodDescription getTarget();
357
358 /**
359 * Representation of an attempt to bind a source method to a target method that is not applicable.
360 *
361 * @see net.bytebuddy.implementation.bind.MethodDelegationBinder
362 */
363 enum Illegal implements MethodBinding {
364
365 /**
366 * The singleton instance.
367 */
368 INSTANCE;
369
370 /**
371 * {@inheritDoc}
372 */
373 public Integer getTargetParameterIndex(Object parameterBindingToken) {
374 throw new IllegalStateException("Method is not bound");
375 }
376
377 /**
378 * {@inheritDoc}
379 */
380 public MethodDescription getTarget() {
381 throw new IllegalStateException("Method is not bound");
382 }
383
384 /**
385 * {@inheritDoc}
386 */
387 public boolean isValid() {
388 return false;
389 }
390
391 /**
392 * {@inheritDoc}
393 */
394 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
395 throw new IllegalStateException("Cannot delegate to an unbound method");
396 }
397 }
398
399 /**
400 * A mutable builder that allows to compose a
401 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding}
402 * by adding parameter bindings incrementally.
403 */
404 class Builder {
405
406 /**
407 * The method invoker for invoking the actual method that is bound.
408 */
409 private final MethodInvoker methodInvoker;
410
411 /**
412 * The target method that for which a binding is to be constructed by this builder..
413 */
414 private final MethodDescription candidate;
415
416 /**
417 * The current list of stack manipulations for loading values for each parameter onto the operand stack.
418 */
419 private final List<StackManipulation> parameterStackManipulations;
420
421 /**
422 * A mapping of identification tokens to the parameter index they were bound for.
423 */
424 private final LinkedHashMap<Object, Integer> registeredTargetIndices;
425
426 /**
427 * The index of the next parameter that is to be bound.
428 */
429 private int nextParameterIndex;
430
431 /**
432 * Creates a new builder for the binding of a given method.
433 *
434 * @param methodInvoker The method invoker that is used to create the method invocation of the {@code target} method.
435 * @param candidate The target method that is target of the binding.
436 */
437 public Builder(MethodInvoker methodInvoker, MethodDescription candidate) {
438 this.methodInvoker = methodInvoker;
439 this.candidate = candidate;
440 parameterStackManipulations = new ArrayList<StackManipulation>(candidate.getParameters().size());
441 registeredTargetIndices = new LinkedHashMap<Object, Integer>();
442 nextParameterIndex = 0;
443 }
444
445 /**
446 * Appends a stack manipulation for the next parameter of the target method.
447 *
448 * @param parameterBinding A binding representing the next subsequent parameter of the method.
449 * @return {@code false} if the {@code parameterBindingToken} was already bound. A conflicting binding should
450 * usually abort the attempt of binding a method and this {@code Builder} should be discarded.
451 */
452 public boolean append(ParameterBinding<?> parameterBinding) {
453 parameterStackManipulations.add(parameterBinding);
454 return registeredTargetIndices.put(parameterBinding.getIdentificationToken(), nextParameterIndex++) == null;
455 }
456
457 /**
458 * Creates a binding that represents the bindings collected by this {@code Builder}.
459 *
460 * @param terminatingManipulation A stack manipulation that is applied after the method invocation.
461 * @return A binding representing the parameter bindings collected by this builder.
462 */
463 public MethodBinding build(StackManipulation terminatingManipulation) {
464 if (candidate.getParameters().size() != nextParameterIndex) {
465 throw new IllegalStateException("The number of parameters bound does not equal the target's number of parameters");
466 }
467 return new Build(candidate,
468 registeredTargetIndices,
469 methodInvoker.invoke(candidate),
470 parameterStackManipulations,
471 terminatingManipulation);
472 }
473
474 /**
475 * A method binding that was created by a
476 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding.Builder}.
477 */
478 @HashCodeAndEqualsPlugin.Enhance
479 protected static class Build implements MethodBinding {
480
481 /**
482 * The target method this binding represents.
483 */
484 private final MethodDescription target;
485
486 /**
487 * A map of identification tokens to the indices of their binding parameters.
488 */
489 private final Map<?, Integer> registeredTargetIndices;
490
491 /**
492 * A stack manipulation that represents the actual method invocation.
493 */
494 private final StackManipulation methodInvocation;
495
496 /**
497 * A list of manipulations that each represent the loading of a parameter value onto the operand stack.
498 */
499 private final List<StackManipulation> parameterStackManipulations;
500
501 /**
502 * The stack manipulation that is applied after the method invocation.
503 */
504 private final StackManipulation terminatingStackManipulation;
505
506 /**
507 * Creates a new method binding.
508 *
509 * @param target The target method this binding represents.
510 * @param registeredTargetIndices A map of identification tokens to the indices of their binding
511 * parameters.
512 * @param methodInvocation A stack manipulation that represents the actual method invocation.
513 * @param parameterStackManipulations A list of manipulations that each represent the loading of a
514 * parameter value onto the operand stack.
515 * @param terminatingStackManipulation The stack manipulation that is applied after the method invocation.
516 */
517 protected Build(MethodDescription target,
518 Map<?, Integer> registeredTargetIndices,
519 StackManipulation methodInvocation,
520 List<StackManipulation> parameterStackManipulations,
521 StackManipulation terminatingStackManipulation) {
522 this.target = target;
523 this.registeredTargetIndices = new HashMap<Object, Integer>(registeredTargetIndices);
524 this.methodInvocation = methodInvocation;
525 this.parameterStackManipulations = new ArrayList<StackManipulation>(parameterStackManipulations);
526 this.terminatingStackManipulation = terminatingStackManipulation;
527 }
528
529 /**
530 * {@inheritDoc}
531 */
532 public boolean isValid() {
533 boolean result = methodInvocation.isValid() && terminatingStackManipulation.isValid();
534 Iterator<StackManipulation> assignment = parameterStackManipulations.iterator();
535 while (result && assignment.hasNext()) {
536 result = assignment.next().isValid();
537 }
538 return result;
539 }
540
541 /**
542 * {@inheritDoc}
543 */
544 public Integer getTargetParameterIndex(Object parameterBindingToken) {
545 return registeredTargetIndices.get(parameterBindingToken);
546 }
547
548 /**
549 * {@inheritDoc}
550 */
551 public MethodDescription getTarget() {
552 return target;
553 }
554
555 /**
556 * {@inheritDoc}
557 */
558 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
559 return new Compound(
560 CompoundList.of(parameterStackManipulations, Arrays.asList(methodInvocation, terminatingStackManipulation))
561 ).apply(methodVisitor, implementationContext);
562 }
563 }
564 }
565 }
566
567 /**
568 * A binding resolver is responsible to choose a method binding between several possible candidates.
569 */
570 interface BindingResolver {
571
572 /**
573 * Resolves a method binding for the {@code source} method.
574 *
575 * @param ambiguityResolver The ambiguity resolver to use.
576 * @param source The source method being bound.
577 * @param targets The possible target candidates. The list contains at least one element.
578 * @return The method binding that was chosen.
579 */
580 MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets);
581
582 /**
583 * A default implementation of a binding resolver that fully relies on an {@link AmbiguityResolver}.
584 */
585 enum Default implements BindingResolver {
586
587 /**
588 * The singleton instance.
589 */
590 INSTANCE;
591
592 /**
593 * Represents the index of the only value of two elements in a list.
594 */
595 private static final int ONLY = 0;
596
597 /**
598 * Represents the index of the left value of two elements in a list.
599 */
600 private static final int LEFT = 0;
601
602 /**
603 * Represents the index of the right value of two elements in a list.
604 */
605 private static final int RIGHT = 1;
606
607 /**
608 * {@inheritDoc}
609 */
610 public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
611 return doResolve(ambiguityResolver, source, new ArrayList<MethodBinding>(targets));
612 }
613
614 /**
615 * Resolves a method binding for the {@code source} method.
616 *
617 * @param ambiguityResolver The ambiguity resolver to use.
618 * @param source The source method being bound.
619 * @param targets The possible target candidates. The list contains at least one element and is mutable.
620 * @return The method binding that was chosen.
621 */
622 private MethodBinding doResolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
623 switch (targets.size()) {
624 case 1:
625 return targets.get(ONLY);
626 case 2: {
627 MethodBinding left = targets.get(LEFT);
628 MethodBinding right = targets.get(RIGHT);
629 switch (ambiguityResolver.resolve(source, left, right)) {
630 case LEFT:
631 return left;
632 case RIGHT:
633 return right;
634 case AMBIGUOUS:
635 case UNKNOWN:
636 throw new IllegalArgumentException("Cannot resolve ambiguous delegation of " + source + " to " + left.getTarget() + " or " + right.getTarget());
637 default:
638 throw new AssertionError();
639 }
640 }
641 default: /* case 3+: */ {
642 MethodBinding left = targets.get(LEFT);
643 MethodBinding right = targets.get(RIGHT);
644 switch (ambiguityResolver.resolve(source, left, right)) {
645 case LEFT:
646 targets.remove(RIGHT);
647 return doResolve(ambiguityResolver, source, targets);
648 case RIGHT:
649 targets.remove(LEFT);
650 return doResolve(ambiguityResolver, source, targets);
651 case AMBIGUOUS:
652 case UNKNOWN:
653 targets.remove(RIGHT); // Remove right element first due to index alteration!
654 targets.remove(LEFT);
655 MethodBinding subResult = doResolve(ambiguityResolver, source, targets);
656 switch (ambiguityResolver.resolve(source, left, subResult).merge(ambiguityResolver.resolve(source, right, subResult))) {
657 case RIGHT:
658 return subResult;
659 case LEFT:
660 case AMBIGUOUS:
661 case UNKNOWN:
662 throw new IllegalArgumentException("Cannot resolve ambiguous delegation of " + source + " to " + left.getTarget() + " or " + right.getTarget());
663 default:
664 throw new AssertionError();
665 }
666 default:
667 throw new IllegalStateException("Unexpected amount of targets: " + targets.size());
668 }
669 }
670 }
671 }
672 }
673
674 /**
675 * A binding resolver that only binds a method if it has a unique binding.
676 */
677 enum Unique implements BindingResolver {
678
679 /**
680 * The singleton instance.
681 */
682 INSTANCE;
683
684 /**
685 * Indicates the first index of a list only containing one element.
686 */
687 private static final int ONLY = 0;
688
689 /**
690 * {@inheritDoc}
691 */
692 public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
693 if (targets.size() == 1) {
694 return targets.get(ONLY);
695 } else {
696 throw new IllegalStateException(source + " allowed for more than one binding: " + targets);
697 }
698 }
699 }
700
701 /**
702 * Binds a method using another resolver and prints the selected binding to a {@link PrintStream}.
703 */
704 @HashCodeAndEqualsPlugin.Enhance
705 class StreamWriting implements BindingResolver {
706
707 /**
708 * The delegate binding resolver.
709 */
710 private final BindingResolver delegate;
711
712 /**
713 * The print stream to bind write the chosen binding to.
714 */
715 private final PrintStream printStream;
716
717 /**
718 * Creates a new stream writing binding resolver.
719 *
720 * @param delegate The delegate binding resolver.
721 * @param printStream The print stream to bind write the chosen binding to.
722 */
723 public StreamWriting(BindingResolver delegate, PrintStream printStream) {
724 this.delegate = delegate;
725 this.printStream = printStream;
726 }
727
728 /**
729 * Creates a binding resolver that writes results to {@link System#out} and delegates to the {@link Default} resolver.
730 *
731 * @return An appropriate binding resolver.
732 */
733 public static BindingResolver toSystemOut() {
734 return toSystemOut(Default.INSTANCE);
735 }
736
737 /**
738 * Creates a binding resolver that writes results to {@link System#out} and delegates to the {@link Default} resolver.
739 *
740 * @param bindingResolver The delegate binding resolver.
741 * @return An appropriate binding resolver.
742 */
743 public static BindingResolver toSystemOut(BindingResolver bindingResolver) {
744 return new StreamWriting(bindingResolver, System.out);
745 }
746
747 /**
748 * Creates a binding resolver that writes results to {@link System#err} and delegates to the {@link Default} resolver.
749 *
750 * @return An appropriate binding resolver.
751 */
752 public static BindingResolver toSystemError() {
753 return toSystemError(Default.INSTANCE);
754 }
755
756 /**
757 * Creates a binding resolver that writes results to {@link System#err}.
758 *
759 * @param bindingResolver The delegate binding resolver.
760 * @return An appropriate binding resolver.
761 */
762 public static BindingResolver toSystemError(BindingResolver bindingResolver) {
763 return new StreamWriting(bindingResolver, System.err);
764 }
765
766 /**
767 * {@inheritDoc}
768 */
769 public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
770 MethodBinding methodBinding = delegate.resolve(ambiguityResolver, source, targets);
771 printStream.println("Binding " + source + " as delegation to " + methodBinding.getTarget());
772 return methodBinding;
773 }
774 }
775 }
776
777 /**
778 * Implementations of this interface are able to attempt the resolution of two successful bindings of a method
779 * to two different target methods in order to identify a dominating binding.
780 */
781 @SuppressFBWarnings(value = "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION", justification = "Safe initialization is implied")
782 interface AmbiguityResolver {
783
784 /**
785 * The default ambiguity resolver to use.
786 */
787 AmbiguityResolver DEFAULT = new MethodDelegationBinder.AmbiguityResolver.Compound(BindingPriority.Resolver.INSTANCE,
788 DeclaringTypeResolver.INSTANCE,
789 ArgumentTypeResolver.INSTANCE,
790 MethodNameEqualityResolver.INSTANCE,
791 ParameterLengthResolver.INSTANCE);
792
793 /**
794 * Attempts to resolve to conflicting bindings.
795 *
796 * @param source The source method that was bound to both target methods.
797 * @param left The first successful binding of the {@code source} method.
798 * @param right The second successful binding of the {@code source} method.
799 * @return The resolution state when resolving a conflicting binding where
800 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#LEFT}
801 * indicates a successful binding to the {@code left} binding while
802 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#RIGHT}
803 * indicates a successful binding to the {@code right} binding.
804 */
805 Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right);
806
807 /**
808 * A resolution state of an attempt to resolve two conflicting bindings.
809 */
810 enum Resolution {
811
812 /**
813 * Describes a resolution state where no information about dominance could be gathered.
814 */
815 UNKNOWN(true),
816
817 /**
818 * Describes a resolution state where the left method dominates the right method.
819 */
820 LEFT(false),
821
822 /**
823 * Describes a resolution state where the right method dominates the left method.
824 */
825 RIGHT(false),
826
827 /**
828 * Describes a resolution state where both methods have inflicting dominance over each other.
829 */
830 AMBIGUOUS(true);
831
832 /**
833 * {@code true} if this resolution is unresolved.
834 */
835 private final boolean unresolved;
836
837 /**
838 * Creates a new resolution.
839 *
840 * @param unresolved {@code true} if this resolution is unresolved.
841 */
842 Resolution(boolean unresolved) {
843 this.unresolved = unresolved;
844 }
845
846 /**
847 * Checks if this binding is unresolved.
848 *
849 * @return {@code true} if this binding is unresolved.
850 */
851 public boolean isUnresolved() {
852 return unresolved;
853 }
854
855 /**
856 * Merges two resolutions in order to determine their compatibility.
857 *
858 * @param other The resolution this resolution is to be checked against.
859 * @return The merged resolution.
860 */
861 public Resolution merge(Resolution other) {
862 switch (this) {
863 case UNKNOWN:
864 return other;
865 case AMBIGUOUS:
866 return AMBIGUOUS;
867 case LEFT:
868 case RIGHT:
869 return other == UNKNOWN || other == this
870 ? this
871 : AMBIGUOUS;
872 default:
873 throw new AssertionError();
874 }
875 }
876 }
877
878 /**
879 * An ambiguity resolver that does not attempt to resolve a conflicting binding.
880 */
881 enum NoOp implements AmbiguityResolver {
882
883 /**
884 * The singleton instance.
885 */
886 INSTANCE;
887
888 /**
889 * {@inheritDoc}
890 */
891 public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) {
892 return Resolution.UNKNOWN;
893 }
894 }
895
896 /**
897 * An ambiguity resolver that always resolves in the specified direction.
898 */
899 enum Directional implements AmbiguityResolver {
900
901 /**
902 * A resolver that always resolves to
903 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#LEFT}.
904 */
905 LEFT(true),
906
907 /**
908 * A resolver that always resolves to
909 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#RIGHT}.
910 */
911 RIGHT(false);
912
913 /**
914 * {@code true} if this instance should resolve to the left side.
915 */
916 private final boolean left;
917
918 /**
919 * Creates a new directional resolver.
920 *
921 * @param left {@code true} if this instance should resolve to the left side.
922 */
923 Directional(boolean left) {
924 this.left = left;
925 }
926
927 /**
928 * {@inheritDoc}
929 */
930 public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) {
931 return this.left
932 ? Resolution.LEFT
933 : Resolution.RIGHT;
934 }
935 }
936
937 /**
938 * A chain of {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}s
939 * that are applied in the given order until two bindings can be resolved.
940 */
941 @HashCodeAndEqualsPlugin.Enhance
942 class Compound implements AmbiguityResolver {
943
944 /**
945 * A list of ambiguity resolvers that are applied by this chain in their order of application.
946 */
947 private final List<AmbiguityResolver> ambiguityResolvers;
948
949 /**
950 * Creates an immutable chain of ambiguity resolvers.
951 *
952 * @param ambiguityResolver The ambiguity resolvers to chain in the order of their application.
953 */
954 public Compound(AmbiguityResolver... ambiguityResolver) {
955 this(Arrays.asList(ambiguityResolver));
956 }
957
958 /**
959 * Creates an immutable chain of ambiguity resolvers.
960 *
961 * @param ambiguityResolvers The ambiguity resolvers to chain in the order of their application.
962 */
963 public Compound(List<? extends AmbiguityResolver> ambiguityResolvers) {
964 this.ambiguityResolvers = new ArrayList<AmbiguityResolver>();
965 for (AmbiguityResolver ambiguityResolver : ambiguityResolvers) {
966 if (ambiguityResolver instanceof Compound) {
967 this.ambiguityResolvers.addAll(((Compound) ambiguityResolver).ambiguityResolvers);
968 } else if (!(ambiguityResolver instanceof NoOp)) {
969 this.ambiguityResolvers.add(ambiguityResolver);
970 }
971 }
972 }
973
974 /**
975 * {@inheritDoc}
976 */
977 public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) {
978 Resolution resolution = Resolution.UNKNOWN;
979 Iterator<? extends AmbiguityResolver> iterator = ambiguityResolvers.iterator();
980 while (resolution.isUnresolved() && iterator.hasNext()) {
981 resolution = iterator.next().resolve(source, left, right);
982 }
983 return resolution;
984 }
985 }
986 }
987
988 /**
989 * A termination handler is responsible for terminating a method delegation.
990 */
991 interface TerminationHandler {
992
993 /**
994 * Creates a stack manipulation that is to be applied after the method return.
995 *
996 * @param assigner The supplied assigner.
997 * @param typing The typing to apply.
998 * @param source The source method that is bound to the {@code target} method.
999 * @param target The target method that is subject to be bound by the {@code source} method.
1000 * @return A stack manipulation that is applied after the method return.
1001 */
1002 StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target);
1003
1004 /**
1005 * Responsible for creating a {@link StackManipulation}
1006 * that is applied after the interception method is applied.
1007 */
1008 enum Default implements TerminationHandler {
1009
1010 /**
1011 * A termination handler that returns the delegate method's return value.
1012 */
1013 RETURNING {
1014 /** {@inheritDoc} */
1015 public StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target) {
1016 return new StackManipulation.Compound(assigner.assign(target.isConstructor()
1017 ? target.getDeclaringType().asGenericType()
1018 : target.getReturnType(),
1019 source.getReturnType(),
1020 typing), MethodReturn.of(source.getReturnType()));
1021 }
1022 },
1023
1024 /**
1025 * A termination handler that drops the delegate method's return value.
1026 */
1027 DROPPING {
1028 /** {@inheritDoc} */
1029 public StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target) {
1030 return Removal.of(target.isConstructor()
1031 ? target.getDeclaringType()
1032 : target.getReturnType());
1033 }
1034 };
1035 }
1036 }
1037
1038 /**
1039 * A helper class that allows to identify a best binding for a given type and source method choosing from a list of given
1040 * target methods by using a given {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}
1041 * and an {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}.
1042 * <p> </p>
1043 * The {@code Processor} will:
1044 * <ol>
1045 * <li>Try to bind the {@code source} method using the {@code MethodDelegationBinder}.</li>
1046 * <li>Find a best method among the successful bindings using the {@code AmbiguityResolver}.</li>
1047 * </ol>
1048 */
1049 @HashCodeAndEqualsPlugin.Enhance
1050 class Processor implements MethodDelegationBinder.Record {
1051
1052 /**
1053 * The delegation records to consider.
1054 */
1055 private final List<? extends Record> records;
1056
1057 /**
1058 * The processor's ambiguity resolver.
1059 */
1060 private final AmbiguityResolver ambiguityResolver;
1061
1062 /**
1063 * The binding resolver being used to select the relevant method binding.
1064 */
1065 private final BindingResolver bindingResolver;
1066
1067 /**
1068 * Creates a new processor.
1069 *
1070 * @param records The delegation records to consider.
1071 * @param ambiguityResolver The ambiguity resolver to apply.
1072 * @param bindingResolver The binding resolver being used to select the relevant method binding.
1073 */
1074 public Processor(List<? extends Record> records, AmbiguityResolver ambiguityResolver, BindingResolver bindingResolver) {
1075 this.records = records;
1076 this.ambiguityResolver = ambiguityResolver;
1077 this.bindingResolver = bindingResolver;
1078 }
1079
1080 /**
1081 * {@inheritDoc}
1082 */
1083 public MethodBinding bind(Implementation.Target implementationTarget,
1084 MethodDescription source,
1085 TerminationHandler terminationHandler,
1086 MethodInvoker methodInvoker,
1087 Assigner assigner) {
1088 List<MethodBinding> targets = new ArrayList<MethodBinding>();
1089 for (Record record : records) {
1090 MethodBinding methodBinding = record.bind(implementationTarget, source, terminationHandler, methodInvoker, assigner);
1091 if (methodBinding.isValid()) {
1092 targets.add(methodBinding);
1093 }
1094 }
1095 if (targets.isEmpty()) {
1096 throw new IllegalArgumentException("None of " + records + " allows for delegation from " + source);
1097 }
1098 return bindingResolver.resolve(ambiguityResolver, source, targets);
1099 }
1100 }
1101 }
1102