• 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

87.04
/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/DefaultMethodCall.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;
17

18
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19
import net.bytebuddy.description.method.MethodDescription;
20
import net.bytebuddy.description.type.TypeDescription;
21
import net.bytebuddy.description.type.TypeList;
22
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
23
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
24
import net.bytebuddy.implementation.bytecode.StackManipulation;
25
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
26
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
27
import org.objectweb.asm.MethodVisitor;
28

29
import java.util.ArrayList;
30
import java.util.Arrays;
31
import java.util.Collection;
32
import java.util.Collections;
33
import java.util.HashSet;
34
import java.util.List;
35
import java.util.Set;
36

37
/**
38
 * This {@link Implementation} invokes a default method for the methods it instruments.
39
 * A default method is potentially ambiguous if a method of identical signature is defined in several interfaces.
40
 * For this reason, this implementation allows for the specification of <i>prioritized interfaces</i> whose default
41
 * methods, if a method of equivalent signature is defined for a specific interface. All prioritized interfaces are
42
 * searched for default methods in the order of their specification. If no prioritized interface defines a default method
43
 * of equivalent signature to the given instrumented method, any non-prioritized interface is searched for a suitable
44
 * default method. If exactly one of those interfaces defines a suitable default method, this method is invoked.
45
 * If no method or more than one method is identified as a suitable default method, an exception is thrown.
46
 * <p>&nbsp;</p>
47
 * When it comes to default methods, the Java programming language specifies stronger requirements for the
48
 * legitimacy of invoking a default method than the Java runtime. The Java compiler requires a method to be
49
 * the most specific method in its defining type's type hierarchy, i.e. the method must not be overridden by another
50
 * interface or class type. Additionally, the method must be invokable from an interface type which is directly
51
 * implemented by the instrumented type. The Java runtime only requires the second condition to be fulfilled which
52
 * is why this implementation only checks the later condition, as well.
53
 */
54
@HashCodeAndEqualsPlugin.Enhance
55
public class DefaultMethodCall implements Implementation {
56

57
    /**
58
     * A list of prioritized interfaces in the order in which a method should be attempted to be called.
59
     */
60
    private final List<TypeDescription> prioritizedInterfaces;
61

62
    /**
63
     * Creates a new {@link net.bytebuddy.implementation.DefaultMethodCall} implementation for a given list of
64
     * prioritized interfaces.
65
     *
66
     * @param prioritizedInterfaces A list of prioritized interfaces in the order in which a method should be attempted to
67
     *                              be called.
68
     */
69
    protected DefaultMethodCall(List<TypeDescription> prioritizedInterfaces) {
1✔
70
        this.prioritizedInterfaces = prioritizedInterfaces;
1✔
71
    }
1✔
72

73
    /**
74
     * Creates a {@link net.bytebuddy.implementation.DefaultMethodCall} implementation which searches the given list
75
     * of interface types for a suitable default method in their order. If no such prioritized interface is suitable,
76
     * because it is either not defined on the instrumented type or because it does not define a suitable default method,
77
     * any remaining interface is searched for a suitable default method. If no or more than one method defines a
78
     * suitable default method, an exception is thrown.
79
     *
80
     * @param prioritizedInterface A list of prioritized default method interfaces in their prioritization order.
81
     * @return An implementation which calls an instrumented method's compatible default method that considers the given
82
     * interfaces to be prioritized in their order.
83
     */
84
    public static Implementation prioritize(Class<?>... prioritizedInterface) {
85
        return prioritize(new TypeList.ForLoadedTypes(prioritizedInterface));
1✔
86
    }
87

88
    /**
89
     * Creates a {@link net.bytebuddy.implementation.DefaultMethodCall} implementation which searches the given list
90
     * of interface types for a suitable default method in their order. If no such prioritized interface is suitable,
91
     * because it is either not defined on the instrumented type or because it does not define a suitable default method,
92
     * any remaining interface is searched for a suitable default method. If no or more than one method defines a
93
     * suitable default method, an exception is thrown.
94
     *
95
     * @param prioritizedInterfaces A list of prioritized default method interfaces in their prioritization order.
96
     * @return An implementation which calls an instrumented method's compatible default method that considers the given
97
     * interfaces to be prioritized in their order.
98
     */
99
    public static Implementation prioritize(Iterable<? extends Class<?>> prioritizedInterfaces) {
100
        List<Class<?>> list = new ArrayList<Class<?>>();
×
101
        for (Class<?> prioritizedInterface : prioritizedInterfaces) {
×
102
            list.add(prioritizedInterface);
×
103
        }
×
104
        return prioritize(new TypeList.ForLoadedTypes(list));
×
105
    }
106

107
    /**
108
     * Creates a {@link net.bytebuddy.implementation.DefaultMethodCall} implementation which searches the given list
109
     * of interface types for a suitable default method in their order. If no such prioritized interface is suitable,
110
     * because it is either not defined on the instrumented type or because it does not define a suitable default method,
111
     * any remaining interface is searched for a suitable default method. If no or more than one method defines a
112
     * suitable default method, an exception is thrown.
113
     *
114
     * @param prioritizedInterface A list of prioritized default method interfaces in their prioritization order.
115
     * @return An implementation which calls an instrumented method's compatible default method that considers the given
116
     * interfaces to be prioritized in their order.
117
     */
118
    public static Implementation prioritize(TypeDescription... prioritizedInterface) {
119
        return prioritize(Arrays.asList(prioritizedInterface));
×
120
    }
121

122
    /**
123
     * Creates a {@link net.bytebuddy.implementation.DefaultMethodCall} implementation which searches the given list
124
     * of interface types for a suitable default method in their order. If no such prioritized interface is suitable,
125
     * because it is either not defined on the instrumented type or because it does not define a suitable default method,
126
     * any remaining interface is searched for a suitable default method. If no or more than one method defines a
127
     * suitable default method, an exception is thrown.
128
     *
129
     * @param prioritizedInterfaces A collection of prioritized default method interfaces in their prioritization order.
130
     * @return An implementation which calls an instrumented method's compatible default method that considers the given
131
     * interfaces to be prioritized in their order.
132
     */
133
    public static Implementation prioritize(Collection<? extends TypeDescription> prioritizedInterfaces) {
134
        return new DefaultMethodCall(new ArrayList<TypeDescription>(prioritizedInterfaces));
1✔
135
    }
136

137
    /**
138
     * Creates a {@link net.bytebuddy.implementation.DefaultMethodCall} implementation without prioritizing any
139
     * interface. Instead, any interface that is defined for a given type is searched for a suitable default method. If
140
     * exactly one interface defines a suitable default method, this method is invoked from the instrumented method.
141
     * Otherwise, an exception is thrown.
142
     *
143
     * @return An implementation which calls an instrumented method's compatible default method if such a method
144
     * is unambiguous.
145
     */
146
    public static Implementation unambiguousOnly() {
147
        return new DefaultMethodCall(Collections.<TypeDescription>emptyList());
1✔
148
    }
149

150
    /**
151
     * {@inheritDoc}
152
     */
153
    public InstrumentedType prepare(InstrumentedType instrumentedType) {
154
        return instrumentedType;
1✔
155
    }
156

157
    /**
158
     * {@inheritDoc}
159
     */
160
    public ByteCodeAppender appender(Target implementationTarget) {
161
        return new Appender(implementationTarget, filterRelevant(implementationTarget.getInstrumentedType()));
1✔
162
    }
163

164
    /**
165
     * Filters the relevant prioritized interfaces for a given type, i.e. finds the types that are
166
     * directly declared on a given instrumented type.
167
     *
168
     * @param typeDescription The instrumented type for which the prioritized interfaces are to be filtered.
169
     * @return A list of prioritized interfaces that are additionally implemented by the given type.
170
     */
171
    private List<TypeDescription> filterRelevant(TypeDescription typeDescription) {
172
        List<TypeDescription> filtered = new ArrayList<TypeDescription>(prioritizedInterfaces.size());
1✔
173
        Set<TypeDescription> relevant = new HashSet<TypeDescription>(typeDescription.getInterfaces().asErasures());
1✔
174
        for (TypeDescription prioritizedInterface : prioritizedInterfaces) {
1✔
175
            if (relevant.remove(prioritizedInterface)) {
1✔
176
                filtered.add(prioritizedInterface);
1✔
177
            }
178
        }
1✔
179
        return filtered;
1✔
180
    }
181

182
    /**
183
     * The appender for implementing a {@link net.bytebuddy.implementation.DefaultMethodCall}.
184
     */
185
    @HashCodeAndEqualsPlugin.Enhance
186
    protected static class Appender implements ByteCodeAppender {
187

188
        /**
189
         * The implementation target of this appender.
190
         */
191
        private final Target implementationTarget;
192

193
        /**
194
         * The relevant prioritized interfaces to be considered by this appender.
195
         */
196
        private final List<TypeDescription> prioritizedInterfaces;
197

198
        /**
199
         * The relevant non-prioritized interfaces to be considered by this appender.
200
         */
201
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.IGNORE)
202
        private final Set<TypeDescription> nonPrioritizedInterfaces;
203

204
        /**
205
         * Creates a new appender for implementing a {@link net.bytebuddy.implementation.DefaultMethodCall}.
206
         *
207
         * @param implementationTarget  The implementation target of this appender.
208
         * @param prioritizedInterfaces The relevant prioritized interfaces to be considered by this appender.
209
         */
210
        protected Appender(Target implementationTarget, List<TypeDescription> prioritizedInterfaces) {
1✔
211
            this.implementationTarget = implementationTarget;
1✔
212
            this.prioritizedInterfaces = prioritizedInterfaces;
1✔
213
            this.nonPrioritizedInterfaces = new HashSet<TypeDescription>(implementationTarget.getInstrumentedType().getInterfaces().asErasures());
1✔
214
            nonPrioritizedInterfaces.removeAll(prioritizedInterfaces);
1✔
215
        }
1✔
216

217
        /**
218
         * {@inheritDoc}
219
         */
220
        public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
221
            StackManipulation defaultMethodInvocation = locateDefault(instrumentedMethod);
1✔
222
            if (!defaultMethodInvocation.isValid()) {
1✔
223
                throw new IllegalStateException("Cannot invoke default method on " + instrumentedMethod);
1✔
224
            }
225
            StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
226
                    MethodVariableAccess.allArgumentsOf(instrumentedMethod).prependThisReference(),
1✔
227
                    defaultMethodInvocation,
228
                    MethodReturn.of(instrumentedMethod.getReturnType())
1✔
229
            ).apply(methodVisitor, implementationContext);
1✔
230
            return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
231
        }
232

233
        /**
234
         * Locates a special method invocation to be invoked from a given method.
235
         *
236
         * @param methodDescription The method that is currently instrumented.
237
         * @return A potentially illegal stack manipulation representing the default method invocation for the
238
         * given method.
239
         */
240
        private StackManipulation locateDefault(MethodDescription methodDescription) {
241
            MethodDescription.SignatureToken methodToken = methodDescription.asSignatureToken();
1✔
242
            SpecialMethodInvocation specialMethodInvocation = SpecialMethodInvocation.Illegal.INSTANCE;
1✔
243
            for (TypeDescription typeDescription : prioritizedInterfaces) {
1✔
244
                specialMethodInvocation = implementationTarget
1✔
245
                        .invokeDefault(methodToken, typeDescription)
1✔
246
                        .withCheckedCompatibilityTo(methodDescription.asTypeToken());
1✔
247
                if (specialMethodInvocation.isValid()) {
1✔
248
                    return specialMethodInvocation;
1✔
249
                }
250
            }
×
251
            for (TypeDescription typeDescription : nonPrioritizedInterfaces) {
1✔
252
                SpecialMethodInvocation other = implementationTarget
1✔
253
                        .invokeDefault(methodToken, typeDescription)
1✔
254
                        .withCheckedCompatibilityTo(methodDescription.asTypeToken());
1✔
255
                if (specialMethodInvocation.isValid() && other.isValid()) {
1✔
256
                    throw new IllegalStateException(methodDescription + " has an ambiguous default method with "
1✔
257
                            + other.getMethodDescription() + " and " + specialMethodInvocation.getMethodDescription());
1✔
258
                }
259
                specialMethodInvocation = other;
1✔
260
            }
1✔
261
            return specialMethodInvocation;
1✔
262
        }
263
    }
264
}
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