• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.14
/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/MethodDelegationBinder.java
1
/*
2
 * Copyright 2014 - Present 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.utility.nullability.MaybeNull;
31
import org.objectweb.asm.MethodVisitor;
32

33
import java.io.PrintStream;
34
import java.util.ArrayList;
35
import java.util.Arrays;
36
import java.util.HashMap;
37
import java.util.Iterator;
38
import java.util.LinkedHashMap;
39
import java.util.List;
40
import java.util.Map;
41

42
/**
43
 * A method delegation binder is responsible for creating a method binding for a <i>source method</i> to a
44
 * <i>target method</i>. Such a binding allows to implement the source method by calling the target method.
45
 * <p>&nbsp;</p>
46
 * Usually, an implementation will attempt to bind a specific source method to a set of target method candidates
47
 * where all legal bindings are considered for binding. To chose a specific candidate, an
48
 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}
49
 * will be consulted for selecting a <i>best</i> binding.
50
 */
51
public interface MethodDelegationBinder {
52

53
    /**
54
     * Compiles this method delegation binder for a target method.
55
     *
56
     * @param candidate The target method to bind.
57
     * @return A compiled target for binding.
58
     */
59
    Record compile(MethodDescription candidate);
60

61
    /**
62
     * A method delegation that was compiled to a target method.
63
     */
64
    interface Record {
65

66
        /**
67
         * Attempts a binding of a source method to this compiled target.
68
         *
69
         * @param implementationTarget The target of the current implementation onto which this binding is to be applied.
70
         * @param source               The method that is to be bound to the {@code target} method.
71
         * @param terminationHandler   The termination handler to apply.
72
         * @param methodInvoker        The method invoker to use.
73
         * @param assigner             The assigner to use.
74
         * @return A binding representing this attempt to bind the {@code source} method to the {@code target} method.
75
         */
76
        MethodBinding bind(Implementation.Target implementationTarget,
77
                           MethodDescription source,
78
                           TerminationHandler terminationHandler,
79
                           MethodInvoker methodInvoker,
80
                           Assigner assigner);
81

82
        /**
83
         * A compiled method delegation binder that only yields illegal bindings.
84
         */
85
        enum Illegal implements Record {
1✔
86

87
            /**
88
             * The singleton instance.
89
             */
90
            INSTANCE;
1✔
91

92
            /**
93
             * {@inheritDoc}
94
             */
95
            public MethodBinding bind(Implementation.Target implementationTarget,
96
                                      MethodDescription source,
97
                                      TerminationHandler terminationHandler,
98
                                      MethodInvoker methodInvoker,
99
                                      Assigner assigner) {
100
                return MethodBinding.Illegal.INSTANCE;
1✔
101
            }
102
        }
103
    }
104

105
    /**
106
     * Implementations are used as delegates for invoking a method that was bound
107
     * using a {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}.
108
     */
109
    interface MethodInvoker {
110

111
        /**
112
         * Creates a method invocation for a given method.
113
         *
114
         * @param methodDescription The method to be invoked.
115
         * @return A stack manipulation encapsulating this method invocation.
116
         */
117
        StackManipulation invoke(MethodDescription methodDescription);
118

119
        /**
120
         * A simple method invocation that merely uses the most general form of method invocation as provided by
121
         * {@link net.bytebuddy.implementation.bytecode.member.MethodInvocation}.
122
         */
123
        enum Simple implements MethodInvoker {
1✔
124

125
            /**
126
             * The singleton instance.
127
             */
128
            INSTANCE;
1✔
129

130
            /**
131
             * {@inheritDoc}
132
             */
133
            public StackManipulation invoke(MethodDescription methodDescription) {
134
                return MethodInvocation.invoke(methodDescription);
1✔
135
            }
136
        }
137

138
        /**
139
         * A method invocation that enforces a virtual invocation that is dispatched on a given type.
140
         */
141
        @HashCodeAndEqualsPlugin.Enhance
142
        class Virtual implements MethodInvoker {
143

144
            /**
145
             * The type on which a method should be invoked virtually.
146
             */
147
            private final TypeDescription typeDescription;
148

149
            /**
150
             * Creates an immutable method invoker that dispatches all methods on a given type.
151
             *
152
             * @param typeDescription The type on which the method is invoked by virtual invocation.
153
             */
154
            public Virtual(TypeDescription typeDescription) {
1✔
155
                this.typeDescription = typeDescription;
1✔
156
            }
1✔
157

158
            /**
159
             * {@inheritDoc}
160
             */
161
            public StackManipulation invoke(MethodDescription methodDescription) {
162
                return MethodInvocation.invoke(methodDescription).virtual(typeDescription);
1✔
163
            }
164
        }
165
    }
166

167
    /**
168
     * A binding attempt for a single parameter. Implementations of this type are a suggestion of composing a
169
     * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding}
170
     * by using a
171
     * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding.Builder}.
172
     * However, method bindings can also be composed without this type which is merely a suggestion.
173
     *
174
     * @param <T> The type of the identification token for this parameter binding.
175
     */
176
    interface ParameterBinding<T> extends StackManipulation {
177

178
        /**
179
         * Returns an identification token for this binding.
180
         *
181
         * @return An identification token unique to this binding.
182
         */
183
        T getIdentificationToken();
184

185
        /**
186
         * A singleton representation of an illegal binding for a method parameter. An illegal binding usually
187
         * suggests that a source method cannot be bound to a specific target method.
188
         */
189
        enum Illegal implements ParameterBinding<Void> {
1✔
190

191
            /**
192
             * The singleton instance.
193
             */
194
            INSTANCE;
1✔
195

196
            /**
197
             * {@inheritDoc}
198
             */
199
            public Void getIdentificationToken() {
200
                throw new IllegalStateException("An illegal binding does not define an identification token");
1✔
201
            }
202

203
            /**
204
             * {@inheritDoc}
205
             */
206
            public boolean isValid() {
207
                return false;
1✔
208
            }
209

210
            /**
211
             * {@inheritDoc}
212
             */
213
            public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
214
                throw new IllegalStateException("An illegal parameter binding must not be applied");
1✔
215
            }
216
        }
217

218
        /**
219
         * An anonymous binding of a target method parameter.
220
         */
221
        @HashCodeAndEqualsPlugin.Enhance
222
        class Anonymous implements ParameterBinding<Object> {
223

224
            /**
225
             * A pseudo-token that is not exposed and therefore anonymous.
226
             */
227
            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.IGNORE)
228
            private final Object anonymousToken;
229

230
            /**
231
             * The stack manipulation that represents the loading of the parameter binding onto the stack.
232
             */
233
            private final StackManipulation delegate;
234

235
            /**
236
             * Creates a new, anonymous parameter binding.
237
             *
238
             * @param delegate The stack manipulation that is responsible for loading the parameter value for this
239
             *                 target method parameter onto the stack.
240
             */
241
            public Anonymous(StackManipulation delegate) {
1✔
242
                this.delegate = delegate;
1✔
243
                anonymousToken = new Object();
1✔
244
            }
1✔
245

246
            /**
247
             * {@inheritDoc}
248
             */
249
            public Object getIdentificationToken() {
250
                return anonymousToken;
1✔
251
            }
252

253
            /**
254
             * {@inheritDoc}
255
             */
256
            public boolean isValid() {
257
                return delegate.isValid();
1✔
258
            }
259

260
            /**
261
             * {@inheritDoc}
262
             */
263
            public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
264
                return delegate.apply(methodVisitor, implementationContext);
1✔
265
            }
266
        }
267

268
        /**
269
         * A uniquely identifiable parameter binding for a target method. Such bindings are usually later processed by
270
         * a {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}
271
         * in order to resolve binding conflicts between several bindable target methods to the same source method.
272
         *
273
         * @param <T> The type of the identification token.
274
         * @see net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver
275
         */
276
        @HashCodeAndEqualsPlugin.Enhance
277
        class Unique<T> implements ParameterBinding<T> {
278

279
            /**
280
             * The token that identifies this parameter binding as unique.
281
             */
282
            private final T identificationToken;
283

284
            /**
285
             * The stack manipulation that represents the loading of the parameter binding onto the stack.
286
             */
287
            private final StackManipulation delegate;
288

289
            /**
290
             * Creates a new unique parameter binding representant.
291
             *
292
             * @param delegate            The stack manipulation that loads the argument for this parameter onto the operand stack.
293
             * @param identificationToken The token used for identifying this parameter binding.
294
             */
295
            public Unique(StackManipulation delegate, T identificationToken) {
1✔
296
                this.delegate = delegate;
1✔
297
                this.identificationToken = identificationToken;
1✔
298
            }
1✔
299

300
            /**
301
             * A factory method for creating a unique binding that infers the tokens type.
302
             *
303
             * @param delegate            The stack manipulation delegate.
304
             * @param identificationToken The identification token.
305
             * @param <S>                 The type of the identification token.
306
             * @return A new instance representing this unique binding.
307
             */
308
            public static <S> Unique<S> of(StackManipulation delegate, S identificationToken) {
309
                return new Unique<S>(delegate, identificationToken);
1✔
310
            }
311

312
            /**
313
             * {@inheritDoc}
314
             */
315
            public T getIdentificationToken() {
316
                return identificationToken;
1✔
317
            }
318

319
            /**
320
             * {@inheritDoc}
321
             */
322
            public boolean isValid() {
323
                return delegate.isValid();
1✔
324
            }
325

326
            /**
327
             * {@inheritDoc}
328
             */
329
            public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
330
                return delegate.apply(methodVisitor, implementationContext);
1✔
331
            }
332
        }
333
    }
334

335
    /**
336
     * A binding attempt created by a
337
     * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}.
338
     */
339
    interface MethodBinding extends StackManipulation {
340

341
        /**
342
         * Returns the target method's parameter index for a given parameter binding token.
343
         * <p>&nbsp;</p>
344
         * A binding token can be any object
345
         * that implements valid {@link Object#hashCode()} and {@link Object#equals(Object)} methods in order
346
         * to look up a given binding. This way, two bindings can be evaluated of having performed a similar type of
347
         * binding such that these bindings can be compared and a dominant binding can be identified by an
348
         * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}.
349
         * Furthermore, a binding is implicitly required to insure the uniqueness of such a parameter binding.
350
         *
351
         * @param parameterBindingToken A token which is used to identify a specific unique binding for a given parameter
352
         *                              of the target method.
353
         * @return The target method's parameter index of this binding or {@code null} if no such argument binding
354
         * was applied for this binding.
355
         */
356
        @MaybeNull
357
        Integer getTargetParameterIndex(Object parameterBindingToken);
358

359
        /**
360
         * Returns the target method of the method binding attempt.
361
         *
362
         * @return The target method to which the
363
         */
364
        MethodDescription getTarget();
365

366
        /**
367
         * Representation of an attempt to bind a source method to a target method that is not applicable.
368
         *
369
         * @see net.bytebuddy.implementation.bind.MethodDelegationBinder
370
         */
371
        enum Illegal implements MethodBinding {
1✔
372

373
            /**
374
             * The singleton instance.
375
             */
376
            INSTANCE;
1✔
377

378
            /**
379
             * {@inheritDoc}
380
             */
381
            public Integer getTargetParameterIndex(Object parameterBindingToken) {
382
                throw new IllegalStateException("Method is not bound");
1✔
383
            }
384

385
            /**
386
             * {@inheritDoc}
387
             */
388
            public MethodDescription getTarget() {
389
                throw new IllegalStateException("Method is not bound");
1✔
390
            }
391

392
            /**
393
             * {@inheritDoc}
394
             */
395
            public boolean isValid() {
396
                return false;
1✔
397
            }
398

399
            /**
400
             * {@inheritDoc}
401
             */
402
            public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
403
                throw new IllegalStateException("Cannot delegate to an unbound method");
1✔
404
            }
405
        }
406

407
        /**
408
         * A mutable builder that allows to compose a
409
         * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding}
410
         * by adding parameter bindings incrementally.
411
         */
412
        class Builder {
413

414
            /**
415
             * The method invoker for invoking the actual method that is bound.
416
             */
417
            private final MethodInvoker methodInvoker;
418

419
            /**
420
             * The target method that for which a binding is to be constructed by this builder..
421
             */
422
            private final MethodDescription candidate;
423

424
            /**
425
             * The current list of stack manipulations for loading values for each parameter onto the operand stack.
426
             */
427
            private final List<StackManipulation> parameterStackManipulations;
428

429
            /**
430
             * A mapping of identification tokens to the parameter index they were bound for.
431
             */
432
            private final LinkedHashMap<Object, Integer> registeredTargetIndices;
433

434
            /**
435
             * The index of the next parameter that is to be bound.
436
             */
437
            private int nextParameterIndex;
438

439
            /**
440
             * Creates a new builder for the binding of a given method.
441
             *
442
             * @param methodInvoker The method invoker that is used to create the method invocation of the {@code target} method.
443
             * @param candidate     The target method that is target of the binding.
444
             */
445
            public Builder(MethodInvoker methodInvoker, MethodDescription candidate) {
1✔
446
                this.methodInvoker = methodInvoker;
1✔
447
                this.candidate = candidate;
1✔
448
                parameterStackManipulations = new ArrayList<StackManipulation>(candidate.getParameters().size());
1✔
449
                registeredTargetIndices = new LinkedHashMap<Object, Integer>();
1✔
450
                nextParameterIndex = 0;
1✔
451
            }
1✔
452

453
            /**
454
             * Appends a stack manipulation for the next parameter of the target method.
455
             *
456
             * @param parameterBinding A binding representing the next subsequent parameter of the method.
457
             * @return {@code false} if the {@code parameterBindingToken} was already bound. A conflicting binding should
458
             * usually abort the attempt of binding a method and this {@code Builder} should be discarded.
459
             */
460
            public boolean append(ParameterBinding<?> parameterBinding) {
461
                parameterStackManipulations.add(parameterBinding);
1✔
462
                return registeredTargetIndices.put(parameterBinding.getIdentificationToken(), nextParameterIndex++) == null;
1✔
463
            }
464

465
            /**
466
             * Creates a binding that represents the bindings collected by this {@code Builder}.
467
             *
468
             * @param terminatingManipulation A stack manipulation that is applied after the method invocation.
469
             * @return A binding representing the parameter bindings collected by this builder.
470
             */
471
            public MethodBinding build(StackManipulation terminatingManipulation) {
472
                if (candidate.getParameters().size() != nextParameterIndex) {
1✔
473
                    throw new IllegalStateException("The number of parameters bound does not equal the target's number of parameters");
1✔
474
                }
475
                return new Build(candidate,
1✔
476
                        registeredTargetIndices,
477
                        methodInvoker.invoke(candidate),
1✔
478
                        parameterStackManipulations,
479
                        terminatingManipulation);
480
            }
481

482
            /**
483
             * A method binding that was created by a
484
             * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding.Builder}.
485
             */
486
            @HashCodeAndEqualsPlugin.Enhance
487
            protected static class Build implements MethodBinding {
488

489
                /**
490
                 * The target method this binding represents.
491
                 */
492
                private final MethodDescription target;
493

494
                /**
495
                 * A map of identification tokens to the indices of their binding parameters.
496
                 */
497
                private final Map<?, Integer> registeredTargetIndices;
498

499
                /**
500
                 * A stack manipulation that represents the actual method invocation.
501
                 */
502
                private final StackManipulation methodInvocation;
503

504
                /**
505
                 * A list of manipulations that each represent the loading of a parameter value onto the operand stack.
506
                 */
507
                private final List<StackManipulation> parameterStackManipulations;
508

509
                /**
510
                 * The stack manipulation that is applied after the method invocation.
511
                 */
512
                private final StackManipulation terminatingStackManipulation;
513

514
                /**
515
                 * Creates a new method binding.
516
                 *
517
                 * @param target                       The target method this binding represents.
518
                 * @param registeredTargetIndices      A map of identification tokens to the indices of their binding
519
                 *                                     parameters.
520
                 * @param methodInvocation             A stack manipulation that represents the actual method invocation.
521
                 * @param parameterStackManipulations  A list of manipulations that each represent the loading of a
522
                 *                                     parameter value onto the operand stack.
523
                 * @param terminatingStackManipulation The stack manipulation that is applied after the method invocation.
524
                 */
525
                protected Build(MethodDescription target,
526
                                Map<?, Integer> registeredTargetIndices,
527
                                StackManipulation methodInvocation,
528
                                List<StackManipulation> parameterStackManipulations,
529
                                StackManipulation terminatingStackManipulation) {
1✔
530
                    this.target = target;
1✔
531
                    this.registeredTargetIndices = new HashMap<Object, Integer>(registeredTargetIndices);
1✔
532
                    this.methodInvocation = methodInvocation;
1✔
533
                    this.parameterStackManipulations = new ArrayList<StackManipulation>(parameterStackManipulations);
1✔
534
                    this.terminatingStackManipulation = terminatingStackManipulation;
1✔
535
                }
1✔
536

537
                /**
538
                 * {@inheritDoc}
539
                 */
540
                public boolean isValid() {
541
                    boolean result = methodInvocation.isValid() && terminatingStackManipulation.isValid();
1✔
542
                    Iterator<StackManipulation> assignment = parameterStackManipulations.iterator();
1✔
543
                    while (result && assignment.hasNext()) {
1✔
544
                        result = assignment.next().isValid();
1✔
545
                    }
546
                    return result;
1✔
547
                }
548

549
                /**
550
                 * {@inheritDoc}
551
                 */
552
                @MaybeNull
553
                public Integer getTargetParameterIndex(Object parameterBindingToken) {
554
                    return registeredTargetIndices.get(parameterBindingToken);
1✔
555
                }
556

557
                /**
558
                 * {@inheritDoc}
559
                 */
560
                public MethodDescription getTarget() {
561
                    return target;
1✔
562
                }
563

564
                /**
565
                 * {@inheritDoc}
566
                 */
567
                public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
568
                    return new Compound(
1✔
569
                            CompoundList.of(parameterStackManipulations, Arrays.asList(methodInvocation, terminatingStackManipulation))
1✔
570
                    ).apply(methodVisitor, implementationContext);
1✔
571
                }
572
            }
573
        }
574
    }
575

576
    /**
577
     * A binding resolver is responsible to choose a method binding between several possible candidates.
578
     */
579
    interface BindingResolver {
580

581
        /**
582
         * Resolves a method binding for the {@code source} method.
583
         *
584
         * @param ambiguityResolver The ambiguity resolver to use.
585
         * @param source            The source method being bound.
586
         * @param targets           The possible target candidates. The list contains at least one element.
587
         * @return The method binding that was chosen.
588
         */
589
        MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets);
590

591
        /**
592
         * A default implementation of a binding resolver that fully relies on an {@link AmbiguityResolver}.
593
         */
594
        enum Default implements BindingResolver {
1✔
595

596
            /**
597
             * The singleton instance.
598
             */
599
            INSTANCE;
1✔
600

601
            /**
602
             * Represents the index of the only value of two elements in a list.
603
             */
604
            private static final int ONLY = 0;
605

606
            /**
607
             * Represents the index of the left value of two elements in a list.
608
             */
609
            private static final int LEFT = 0;
610

611
            /**
612
             * Represents the index of the right value of two elements in a list.
613
             */
614
            private static final int RIGHT = 1;
615

616
            /**
617
             * {@inheritDoc}
618
             */
619
            public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
620
                return doResolve(ambiguityResolver, source, new ArrayList<MethodBinding>(targets));
1✔
621
            }
622

623
            /**
624
             * Resolves a method binding for the {@code source} method.
625
             *
626
             * @param ambiguityResolver The ambiguity resolver to use.
627
             * @param source            The source method being bound.
628
             * @param targets           The possible target candidates. The list contains at least one element and is mutable.
629
             * @return The method binding that was chosen.
630
             */
631
            private MethodBinding doResolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
632
                switch (targets.size()) {
1✔
633
                    case 1:
634
                        return targets.get(ONLY);
1✔
635
                    case 2: {
636
                        MethodBinding left = targets.get(LEFT);
1✔
637
                        MethodBinding right = targets.get(RIGHT);
1✔
638
                        switch (ambiguityResolver.resolve(source, left, right)) {
1✔
639
                            case LEFT:
640
                                return left;
1✔
641
                            case RIGHT:
642
                                return right;
1✔
643
                            case AMBIGUOUS:
644
                            case UNKNOWN:
645
                                throw new IllegalArgumentException("Cannot resolve ambiguous delegation of " + source + " to " + left.getTarget() + " or " + right.getTarget());
1✔
646
                            default:
647
                                throw new AssertionError();
×
648
                        }
649
                    }
650
                    default: /* case 3+: */ {
651
                        MethodBinding left = targets.get(LEFT);
1✔
652
                        MethodBinding right = targets.get(RIGHT);
1✔
653
                        switch (ambiguityResolver.resolve(source, left, right)) {
1✔
654
                            case LEFT:
655
                                targets.remove(RIGHT);
1✔
656
                                return doResolve(ambiguityResolver, source, targets);
1✔
657
                            case RIGHT:
658
                                targets.remove(LEFT);
1✔
659
                                return doResolve(ambiguityResolver, source, targets);
1✔
660
                            case AMBIGUOUS:
661
                            case UNKNOWN:
662
                                targets.remove(RIGHT); // Remove right element first due to index alteration!
1✔
663
                                targets.remove(LEFT);
1✔
664
                                MethodBinding subResult = doResolve(ambiguityResolver, source, targets);
1✔
665
                                switch (ambiguityResolver.resolve(source, left, subResult).merge(ambiguityResolver.resolve(source, right, subResult))) {
1✔
666
                                    case RIGHT:
667
                                        return subResult;
1✔
668
                                    case LEFT:
669
                                    case AMBIGUOUS:
670
                                    case UNKNOWN:
671
                                        throw new IllegalArgumentException("Cannot resolve ambiguous delegation of " + source + " to " + left.getTarget() + " or " + right.getTarget());
×
672
                                    default:
673
                                        throw new AssertionError();
×
674
                                }
675
                            default:
676
                                throw new IllegalStateException("Unexpected amount of targets: " + targets.size());
×
677
                        }
678
                    }
679
                }
680
            }
681
        }
682

683
        /**
684
         * A binding resolver that only binds a method if it has a unique binding.
685
         */
686
        enum Unique implements BindingResolver {
1✔
687

688
            /**
689
             * The singleton instance.
690
             */
691
            INSTANCE;
1✔
692

693
            /**
694
             * Indicates the first index of a list only containing one element.
695
             */
696
            private static final int ONLY = 0;
697

698
            /**
699
             * {@inheritDoc}
700
             */
701
            public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
702
                if (targets.size() == 1) {
1✔
703
                    return targets.get(ONLY);
1✔
704
                } else {
705
                    throw new IllegalStateException(source + " allowed for more than one binding: " + targets);
1✔
706
                }
707
            }
708
        }
709

710
        /**
711
         * Binds a method using another resolver and prints the selected binding to a {@link PrintStream}.
712
         */
713
        @HashCodeAndEqualsPlugin.Enhance
714
        class StreamWriting implements BindingResolver {
715

716
            /**
717
             * The delegate binding resolver.
718
             */
719
            private final BindingResolver delegate;
720

721
            /**
722
             * The print stream to bind write the chosen binding to.
723
             */
724
            private final PrintStream printStream;
725

726
            /**
727
             * Creates a new stream writing binding resolver.
728
             *
729
             * @param delegate    The delegate binding resolver.
730
             * @param printStream The print stream to bind write the chosen binding to.
731
             */
732
            public StreamWriting(BindingResolver delegate, PrintStream printStream) {
1✔
733
                this.delegate = delegate;
1✔
734
                this.printStream = printStream;
1✔
735
            }
1✔
736

737
            /**
738
             * Creates a binding resolver that writes results to {@link System#out} and delegates to the {@link Default} resolver.
739
             *
740
             * @return An appropriate binding resolver.
741
             */
742
            public static BindingResolver toSystemOut() {
743
                return toSystemOut(Default.INSTANCE);
1✔
744
            }
745

746
            /**
747
             * Creates a binding resolver that writes results to {@link System#out} and delegates to the {@link Default} resolver.
748
             *
749
             * @param bindingResolver The delegate binding resolver.
750
             * @return An appropriate binding resolver.
751
             */
752
            public static BindingResolver toSystemOut(BindingResolver bindingResolver) {
753
                return new StreamWriting(bindingResolver, System.out);
1✔
754
            }
755

756
            /**
757
             * Creates a binding resolver that writes results to {@link System#err} and delegates to the {@link Default} resolver.
758
             *
759
             * @return An appropriate binding resolver.
760
             */
761
            public static BindingResolver toSystemError() {
762
                return toSystemError(Default.INSTANCE);
1✔
763
            }
764

765
            /**
766
             * Creates a binding resolver that writes results to {@link System#err}.
767
             *
768
             * @param bindingResolver The delegate binding resolver.
769
             * @return An appropriate binding resolver.
770
             */
771
            public static BindingResolver toSystemError(BindingResolver bindingResolver) {
772
                return new StreamWriting(bindingResolver, System.err);
1✔
773
            }
774

775
            /**
776
             * {@inheritDoc}
777
             */
778
            public MethodBinding resolve(AmbiguityResolver ambiguityResolver, MethodDescription source, List<MethodBinding> targets) {
779
                MethodBinding methodBinding = delegate.resolve(ambiguityResolver, source, targets);
1✔
780
                printStream.println("Binding " + source + " as delegation to " + methodBinding.getTarget());
1✔
781
                return methodBinding;
1✔
782
            }
783
        }
784
    }
785

786
    /**
787
     * Implementations of this interface are able to attempt the resolution of two successful bindings of a method
788
     * to two different target methods in order to identify a dominating binding.
789
     */
790
    @SuppressFBWarnings(value = "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION", justification = "Safe initialization is implied.")
791
    interface AmbiguityResolver {
792

793
        /**
794
         * The default ambiguity resolver to use.
795
         */
796
        AmbiguityResolver DEFAULT = new MethodDelegationBinder.AmbiguityResolver.Compound(BindingPriority.Resolver.INSTANCE,
1✔
797
                DeclaringTypeResolver.INSTANCE,
798
                ArgumentTypeResolver.INSTANCE,
799
                MethodNameEqualityResolver.INSTANCE,
800
                ParameterLengthResolver.INSTANCE);
801

802
        /**
803
         * Attempts to resolve to conflicting bindings.
804
         *
805
         * @param source The source method that was bound to both target methods.
806
         * @param left   The first successful binding of the {@code source} method.
807
         * @param right  The second successful binding of the {@code source} method.
808
         * @return The resolution state when resolving a conflicting binding where
809
         * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#LEFT}
810
         * indicates a successful binding to the {@code left} binding while
811
         * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#RIGHT}
812
         * indicates a successful binding to the {@code right} binding.
813
         */
814
        Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right);
815

816
        /**
817
         * A resolution state of an attempt to resolve two conflicting bindings.
818
         */
819
        enum Resolution {
1✔
820

821
            /**
822
             * Describes a resolution state where no information about dominance could be gathered.
823
             */
824
            UNKNOWN(true),
1✔
825

826
            /**
827
             * Describes a resolution state where the left method dominates the right method.
828
             */
829
            LEFT(false),
1✔
830

831
            /**
832
             * Describes a resolution state where the right method dominates the left method.
833
             */
834
            RIGHT(false),
1✔
835

836
            /**
837
             * Describes a resolution state where both methods have inflicting dominance over each other.
838
             */
839
            AMBIGUOUS(true);
1✔
840

841
            /**
842
             * {@code true} if this resolution is unresolved.
843
             */
844
            private final boolean unresolved;
845

846
            /**
847
             * Creates a new resolution.
848
             *
849
             * @param unresolved {@code true} if this resolution is unresolved.
850
             */
851
            Resolution(boolean unresolved) {
1✔
852
                this.unresolved = unresolved;
1✔
853
            }
1✔
854

855
            /**
856
             * Checks if this binding is unresolved.
857
             *
858
             * @return {@code true} if this binding is unresolved.
859
             */
860
            public boolean isUnresolved() {
861
                return unresolved;
1✔
862
            }
863

864
            /**
865
             * Merges two resolutions in order to determine their compatibility.
866
             *
867
             * @param other The resolution this resolution is to be checked against.
868
             * @return The merged resolution.
869
             */
870
            public Resolution merge(Resolution other) {
871
                switch (this) {
1✔
872
                    case UNKNOWN:
873
                        return other;
1✔
874
                    case AMBIGUOUS:
875
                        return AMBIGUOUS;
1✔
876
                    case LEFT:
877
                    case RIGHT:
878
                        return other == UNKNOWN || other == this
1✔
879
                                ? this
880
                                : AMBIGUOUS;
881
                    default:
882
                        throw new AssertionError();
×
883
                }
884
            }
885
        }
886

887
        /**
888
         * An ambiguity resolver that does not attempt to resolve a conflicting binding.
889
         */
890
        enum NoOp implements AmbiguityResolver {
1✔
891

892
            /**
893
             * The singleton instance.
894
             */
895
            INSTANCE;
1✔
896

897
            /**
898
             * {@inheritDoc}
899
             */
900
            public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) {
901
                return Resolution.UNKNOWN;
1✔
902
            }
903
        }
904

905
        /**
906
         * An ambiguity resolver that always resolves in the specified direction.
907
         */
908
        enum Directional implements AmbiguityResolver {
1✔
909

910
            /**
911
             * A resolver that always resolves to
912
             * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#LEFT}.
913
             */
914
            LEFT(true),
1✔
915

916
            /**
917
             * A resolver that always resolves to
918
             * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#RIGHT}.
919
             */
920
            RIGHT(false);
1✔
921

922
            /**
923
             * {@code true} if this instance should resolve to the left side.
924
             */
925
            private final boolean left;
926

927
            /**
928
             * Creates a new directional resolver.
929
             *
930
             * @param left {@code true} if this instance should resolve to the left side.
931
             */
932
            Directional(boolean left) {
1✔
933
                this.left = left;
1✔
934
            }
1✔
935

936
            /**
937
             * {@inheritDoc}
938
             */
939
            public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) {
940
                return this.left
1✔
941
                        ? Resolution.LEFT
942
                        : Resolution.RIGHT;
943
            }
944
        }
945

946
        /**
947
         * A chain of {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}s
948
         * that are applied in the given order until two bindings can be resolved.
949
         */
950
        @HashCodeAndEqualsPlugin.Enhance
951
        class Compound implements AmbiguityResolver {
952

953
            /**
954
             * A list of ambiguity resolvers that are applied by this chain in their order of application.
955
             */
956
            private final List<AmbiguityResolver> ambiguityResolvers;
957

958
            /**
959
             * Creates an immutable chain of ambiguity resolvers.
960
             *
961
             * @param ambiguityResolver The ambiguity resolvers to chain in the order of their application.
962
             */
963
            public Compound(AmbiguityResolver... ambiguityResolver) {
964
                this(Arrays.asList(ambiguityResolver));
1✔
965
            }
1✔
966

967
            /**
968
             * Creates an immutable chain of ambiguity resolvers.
969
             *
970
             * @param ambiguityResolvers The ambiguity resolvers to chain in the order of their application.
971
             */
972
            public Compound(List<? extends AmbiguityResolver> ambiguityResolvers) {
1✔
973
                this.ambiguityResolvers = new ArrayList<AmbiguityResolver>();
1✔
974
                for (AmbiguityResolver ambiguityResolver : ambiguityResolvers) {
1✔
975
                    if (ambiguityResolver instanceof Compound) {
1✔
976
                        this.ambiguityResolvers.addAll(((Compound) ambiguityResolver).ambiguityResolvers);
1✔
977
                    } else if (!(ambiguityResolver instanceof NoOp)) {
1✔
978
                        this.ambiguityResolvers.add(ambiguityResolver);
1✔
979
                    }
980
                }
1✔
981
            }
1✔
982

983
            /**
984
             * {@inheritDoc}
985
             */
986
            public Resolution resolve(MethodDescription source, MethodBinding left, MethodBinding right) {
987
                Resolution resolution = Resolution.UNKNOWN;
1✔
988
                Iterator<? extends AmbiguityResolver> iterator = ambiguityResolvers.iterator();
1✔
989
                while (resolution.isUnresolved() && iterator.hasNext()) {
1✔
990
                    resolution = iterator.next().resolve(source, left, right);
1✔
991
                }
992
                return resolution;
1✔
993
            }
994
        }
995
    }
996

997
    /**
998
     * A termination handler is responsible for terminating a method delegation.
999
     */
1000
    interface TerminationHandler {
1001

1002
        /**
1003
         * Creates a stack manipulation that is to be applied after the method return.
1004
         *
1005
         * @param assigner The supplied assigner.
1006
         * @param typing   The typing to apply.
1007
         * @param source   The source method that is bound to the {@code target} method.
1008
         * @param target   The target method that is subject to be bound by the {@code source} method.
1009
         * @return A stack manipulation that is applied after the method return.
1010
         */
1011
        StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target);
1012

1013
        /**
1014
         * Responsible for creating a {@link StackManipulation}
1015
         * that is applied after the interception method is applied.
1016
         */
1017
        enum Default implements TerminationHandler {
1✔
1018

1019
            /**
1020
             * A termination handler that returns the delegate method's return value.
1021
             */
1022
            RETURNING {
1✔
1023
                /** {@inheritDoc} */
1024
                public StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target) {
1025
                    return new StackManipulation.Compound(assigner.assign(target.isConstructor()
1✔
1026
                                    ? target.getDeclaringType().asGenericType()
1✔
1027
                                    : target.getReturnType(),
1✔
1028
                            source.getReturnType(),
1✔
1029
                            typing), MethodReturn.of(source.getReturnType()));
1✔
1030
                }
1031
            },
1032

1033
            /**
1034
             * A termination handler that drops the delegate method's return value.
1035
             */
1036
            DROPPING {
1✔
1037
                /** {@inheritDoc} */
1038
                public StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target) {
1039
                    return Removal.of(target.isConstructor()
1✔
1040
                            ? target.getDeclaringType()
1✔
1041
                            : target.getReturnType());
1✔
1042
                }
1043
            }
1044
        }
1045
    }
1046

1047
    /**
1048
     * A helper class that allows to identify a best binding for a given type and source method choosing from a list of given
1049
     * target methods by using a given {@link net.bytebuddy.implementation.bind.MethodDelegationBinder}
1050
     * and an {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}.
1051
     * <p>&nbsp;</p>
1052
     * The {@code Processor} will:
1053
     * <ol>
1054
     * <li>Try to bind the {@code source} method using the {@code MethodDelegationBinder}.</li>
1055
     * <li>Find a best method among the successful bindings using the {@code AmbiguityResolver}.</li>
1056
     * </ol>
1057
     */
1058
    @HashCodeAndEqualsPlugin.Enhance
1059
    class Processor implements MethodDelegationBinder.Record {
1060

1061
        /**
1062
         * The delegation records to consider.
1063
         */
1064
        private final List<? extends Record> records;
1065

1066
        /**
1067
         * The processor's ambiguity resolver.
1068
         */
1069
        private final AmbiguityResolver ambiguityResolver;
1070

1071
        /**
1072
         * The binding resolver being used to select the relevant method binding.
1073
         */
1074
        private final BindingResolver bindingResolver;
1075

1076
        /**
1077
         * Creates a new processor.
1078
         *
1079
         * @param records           The delegation records to consider.
1080
         * @param ambiguityResolver The ambiguity resolver to apply.
1081
         * @param bindingResolver   The binding resolver being used to select the relevant method binding.
1082
         */
1083
        public Processor(List<? extends Record> records, AmbiguityResolver ambiguityResolver, BindingResolver bindingResolver) {
1✔
1084
            this.records = records;
1✔
1085
            this.ambiguityResolver = ambiguityResolver;
1✔
1086
            this.bindingResolver = bindingResolver;
1✔
1087
        }
1✔
1088

1089
        /**
1090
         * {@inheritDoc}
1091
         */
1092
        public MethodBinding bind(Implementation.Target implementationTarget,
1093
                                  MethodDescription source,
1094
                                  TerminationHandler terminationHandler,
1095
                                  MethodInvoker methodInvoker,
1096
                                  Assigner assigner) {
1097
            List<MethodBinding> targets = new ArrayList<MethodBinding>();
1✔
1098
            for (Record record : records) {
1✔
1099
                MethodBinding methodBinding = record.bind(implementationTarget, source, terminationHandler, methodInvoker, assigner);
1✔
1100
                if (methodBinding.isValid()) {
1✔
1101
                    targets.add(methodBinding);
1✔
1102
                }
1103
            }
1✔
1104
            if (targets.isEmpty()) {
1✔
1105
                throw new IllegalArgumentException("None of " + records + " allows for delegation from " + source);
1✔
1106
            }
1107
            return bindingResolver.resolve(ambiguityResolver, source, targets);
1✔
1108
        }
1109
    }
1110
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc