• 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.37
/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/auxiliary/MethodCallProxy.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.auxiliary;
17

18
import net.bytebuddy.ByteBuddy;
19
import net.bytebuddy.ClassFileVersion;
20
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
21
import net.bytebuddy.description.annotation.AnnotationDescription;
22
import net.bytebuddy.description.annotation.AnnotationValue;
23
import net.bytebuddy.description.field.FieldDescription;
24
import net.bytebuddy.description.field.FieldList;
25
import net.bytebuddy.description.method.MethodDescription;
26
import net.bytebuddy.description.method.ParameterDescription;
27
import net.bytebuddy.description.modifier.Visibility;
28
import net.bytebuddy.description.type.TypeDefinition;
29
import net.bytebuddy.description.type.TypeDescription;
30
import net.bytebuddy.description.type.TypeVariableToken;
31
import net.bytebuddy.dynamic.DynamicType;
32
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
33
import net.bytebuddy.dynamic.scaffold.MethodGraph;
34
import net.bytebuddy.dynamic.scaffold.TypeValidation;
35
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
36
import net.bytebuddy.implementation.Implementation;
37
import net.bytebuddy.implementation.MethodAccessorFactory;
38
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
39
import net.bytebuddy.implementation.bytecode.Duplication;
40
import net.bytebuddy.implementation.bytecode.StackManipulation;
41
import net.bytebuddy.implementation.bytecode.TypeCreation;
42
import net.bytebuddy.implementation.bytecode.assign.Assigner;
43
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
44
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
45
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
46
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
47
import net.bytebuddy.utility.RandomString;
48
import org.objectweb.asm.MethodVisitor;
49
import org.objectweb.asm.Opcodes;
50

51
import java.io.Serializable;
52
import java.util.ArrayList;
53
import java.util.Collections;
54
import java.util.LinkedHashMap;
55
import java.util.List;
56
import java.util.Map;
57
import java.util.concurrent.Callable;
58

59
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
60

61
/**
62
 * A method call proxy represents a class that is compiled against a particular method which can then be called whenever
63
 * either its {@link java.util.concurrent.Callable#call()} or {@link Runnable#run()} method is called where the method
64
 * call proxy implements both interfaces.
65
 * <p>&nbsp;</p>
66
 * In order to do so, the method call proxy instances are constructed by providing all the necessary information for
67
 * calling a particular method:
68
 * <ol>
69
 * <li>If the target method is not {@code static}, the first argument should be an instance on which the method is called.</li>
70
 * <li>All arguments for the called method in the order in which they are required.</li>
71
 * </ol>
72
 */
73
@HashCodeAndEqualsPlugin.Enhance
74
public class MethodCallProxy implements AuxiliaryType {
75

76
    /**
77
     * The prefix of the fields holding the original method invocation's arguments.
78
     */
79
    private static final String FIELD_NAME_PREFIX = "argument";
80

81
    /**
82
     * The special method invocation to invoke from the auxiliary type.
83
     */
84
    private final Implementation.SpecialMethodInvocation specialMethodInvocation;
85

86
    /**
87
     * Determines if the generated proxy should be serializableProxy.
88
     */
89
    private final boolean serializableProxy;
90

91
    /**
92
     * The assigner to use for invoking a bridge method target where the parameter and return types need to be
93
     * assigned.
94
     */
95
    private final Assigner assigner;
96

97
    /**
98
     * Creates a new method call proxy for a given method and uses a default assigner for assigning the method's return
99
     * value to either the {@link java.util.concurrent.Callable#call()} or {@link Runnable#run()} method returns.
100
     *
101
     * @param specialMethodInvocation The special method invocation which should be invoked by this method call proxy.
102
     * @param serializableProxy       Determines if the generated proxy should be serializableProxy.
103
     */
104
    public MethodCallProxy(Implementation.SpecialMethodInvocation specialMethodInvocation, boolean serializableProxy) {
105
        this(specialMethodInvocation, serializableProxy, Assigner.DEFAULT);
1✔
106
    }
1✔
107

108
    /**
109
     * Creates a new method call proxy for a given method.
110
     *
111
     * @param specialMethodInvocation The special method invocation which should be invoked by this method call proxy.
112
     * @param serializableProxy       Determines if the generated proxy should be serializableProxy.
113
     * @param assigner                An assigner for assigning the target method's return value to either the
114
     *                                {@link java.util.concurrent.Callable#call()} or {@link Runnable#run()}} methods'
115
     *                                return values.
116
     */
117
    public MethodCallProxy(Implementation.SpecialMethodInvocation specialMethodInvocation, boolean serializableProxy, Assigner assigner) {
1✔
118
        this.specialMethodInvocation = specialMethodInvocation;
1✔
119
        this.serializableProxy = serializableProxy;
1✔
120
        this.assigner = assigner;
1✔
121
    }
1✔
122

123
    /**
124
     * Creates a linked hash map of field names to their types where each field represents a parameter of the method.
125
     *
126
     * @param methodDescription The method to extract into fields.
127
     * @return A map of fields in the order they need to be loaded onto the operand stack for invoking the original
128
     * method, including a reference to the instance of the instrumented type that is invoked if applicable.
129
     */
130
    private static LinkedHashMap<String, TypeDescription> extractFields(MethodDescription methodDescription) {
131
        LinkedHashMap<String, TypeDescription> typeDescriptions = new LinkedHashMap<String, TypeDescription>();
1✔
132
        int currentIndex = 0;
1✔
133
        if (!methodDescription.isStatic()) {
1✔
134
            typeDescriptions.put(fieldName(currentIndex++), methodDescription.getDeclaringType().asErasure());
1✔
135
        }
136
        for (ParameterDescription parameterDescription : methodDescription.getParameters()) {
1✔
137
            typeDescriptions.put(fieldName(currentIndex++), parameterDescription.getType().asErasure());
1✔
138
        }
1✔
139
        return typeDescriptions;
1✔
140
    }
141

142
    /**
143
     * Creates a field name for a method parameter of a given index.
144
     *
145
     * @param index The index for which the field name is to be created.
146
     * @return The name for the given parameter.
147
     */
148
    private static String fieldName(int index) {
149
        return FIELD_NAME_PREFIX + index;
1✔
150
    }
151

152
    /**
153
     * {@inheritDoc}
154
     */
155
    public String getSuffix() {
156
        return RandomString.hashOf(specialMethodInvocation.getMethodDescription().hashCode()) + (serializableProxy ? "S" : "0");
1✔
157
    }
158

159
    /**
160
     * {@inheritDoc}
161
     */
162
    public DynamicType make(String auxiliaryTypeName,
163
                            ClassFileVersion classFileVersion,
164
                            MethodAccessorFactory methodAccessorFactory) {
165
        MethodDescription accessorMethod = methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT);
1✔
166
        LinkedHashMap<String, TypeDescription> parameterFields = extractFields(accessorMethod);
1✔
167
        DynamicType.Builder<?> builder = new ByteBuddy(classFileVersion)
1✔
168
                .with(TypeValidation.DISABLED)
1✔
169
                .with(PrecomputedMethodGraph.INSTANCE)
1✔
170
                .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
1✔
171
                .name(auxiliaryTypeName)
1✔
172
                .modifiers(DEFAULT_TYPE_MODIFIER)
1✔
173
                .implement(Runnable.class, Callable.class).intercept(new MethodCall(accessorMethod, assigner))
1✔
174
                .implement(serializableProxy ? new Class<?>[]{Serializable.class} : new Class<?>[0])
1✔
175
                .defineConstructor().withParameters(parameterFields.values())
1✔
176
                .intercept(ConstructorCall.INSTANCE);
1✔
177
        for (Map.Entry<String, TypeDescription> field : parameterFields.entrySet()) {
1✔
178
            builder = builder.defineField(field.getKey(), field.getValue(), Visibility.PRIVATE);
1✔
179
        }
1✔
180
        return builder.make();
1✔
181
    }
182

183
    /**
184
     * A precomputed method graph that only displays the methods that are relevant for creating a method call proxy.
185
     */
186
    protected enum PrecomputedMethodGraph implements MethodGraph.Compiler {
1✔
187

188
        /**
189
         * The singleton instance.
190
         */
191
        INSTANCE;
1✔
192

193
        /**
194
         * The precomputed method graph.
195
         */
196
        private final transient MethodGraph.Linked methodGraph;
197

198
        /**
199
         * Creates the precomputed method graph.
200
         */
201
        PrecomputedMethodGraph() {
1✔
202
            LinkedHashMap<MethodDescription.SignatureToken, MethodGraph.Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, MethodGraph.Node>();
1✔
203
            MethodDescription callMethod = new MethodDescription.Latent(TypeDescription.ForLoadedType.of(Callable.class),
1✔
204
                    "call",
205
                    Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT,
206
                    Collections.<TypeVariableToken>emptyList(),
1✔
207
                    TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(Object.class),
1✔
208
                    Collections.<ParameterDescription.Token>emptyList(),
1✔
209
                    Collections.singletonList(TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(Exception.class)),
1✔
210
                    Collections.<AnnotationDescription>emptyList(),
1✔
211
                    AnnotationValue.UNDEFINED,
212
                    TypeDescription.Generic.UNDEFINED);
213
            nodes.put(callMethod.asSignatureToken(), new MethodGraph.Node.Simple(callMethod));
1✔
214
            MethodDescription runMethod = new MethodDescription.Latent(TypeDescription.ForLoadedType.of(Runnable.class),
1✔
215
                    "run",
216
                    Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT,
217
                    Collections.<TypeVariableToken>emptyList(),
1✔
218
                    TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(void.class),
1✔
219
                    Collections.<ParameterDescription.Token>emptyList(),
1✔
220
                    Collections.<TypeDescription.Generic>emptyList(),
1✔
221
                    Collections.<AnnotationDescription>emptyList(),
1✔
222
                    AnnotationValue.UNDEFINED,
223
                    TypeDescription.Generic.UNDEFINED);
224
            nodes.put(runMethod.asSignatureToken(), new MethodGraph.Node.Simple(runMethod));
1✔
225
            MethodGraph methodGraph = new MethodGraph.Simple(nodes);
1✔
226
            this.methodGraph = new MethodGraph.Linked.Delegation(methodGraph, methodGraph, Collections.<TypeDescription, MethodGraph>emptyMap());
1✔
227
        }
1✔
228

229
        /**
230
         * {@inheritDoc}
231
         */
232
        public MethodGraph.Linked compile(TypeDefinition typeDefinition) {
233
            return methodGraph;
1✔
234
        }
235

236
        /**
237
         * {@inheritDoc}
238
         */
239
        @Deprecated
240
        public MethodGraph.Linked compile(TypeDescription typeDescription) {
241
            return methodGraph;
×
242
        }
243

244
        /**
245
         * {@inheritDoc}
246
         */
247
        public MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
248
            return methodGraph;
×
249
        }
250

251
        /**
252
         * {@inheritDoc}
253
         */
254
        @Deprecated
255
        public MethodGraph.Linked compile(TypeDescription typeDefinition, TypeDescription viewPoint) {
256
            return methodGraph;
×
257
        }
258
    }
259

260
    /**
261
     * An implementation for a constructor of a {@link net.bytebuddy.implementation.auxiliary.MethodCallProxy}.
262
     */
263
    protected enum ConstructorCall implements Implementation {
1✔
264

265
        /**
266
         * The singleton instance.
267
         */
268
        INSTANCE;
1✔
269

270
        /**
271
         * A reference of the {@link Object} type default constructor.
272
         */
273
        private final MethodDescription objectTypeDefaultConstructor;
274

275
        /**
276
         * Creates the constructor call singleton.
277
         */
278
        ConstructorCall() {
1✔
279
            objectTypeDefaultConstructor = TypeDescription.ForLoadedType.of(Object.class).getDeclaredMethods().filter(isConstructor()).getOnly();
1✔
280
        }
1✔
281

282
        /**
283
         * {@inheritDoc}
284
         */
285
        public InstrumentedType prepare(InstrumentedType instrumentedType) {
286
            return instrumentedType;
1✔
287
        }
288

289
        /**
290
         * {@inheritDoc}
291
         */
292
        public ByteCodeAppender appender(Target implementationTarget) {
293
            return new Appender(implementationTarget.getInstrumentedType());
1✔
294
        }
295

296
        /**
297
         * The appender for implementing the {@link net.bytebuddy.implementation.auxiliary.MethodCallProxy.ConstructorCall}.
298
         */
299
        @HashCodeAndEqualsPlugin.Enhance
300
        protected static class Appender implements ByteCodeAppender {
301

302
            /**
303
             * The instrumented type being created.
304
             */
305
            private final TypeDescription instrumentedType;
306

307
            /**
308
             * Creates a new appender.
309
             *
310
             * @param instrumentedType The instrumented type that is being created.
311
             */
312
            private Appender(TypeDescription instrumentedType) {
1✔
313
                this.instrumentedType = instrumentedType;
1✔
314
            }
1✔
315

316
            /**
317
             * {@inheritDoc}
318
             */
319
            public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
320
                FieldList<?> fieldList = instrumentedType.getDeclaredFields();
1✔
321
                StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()];
1✔
322
                int index = 0;
1✔
323
                for (FieldDescription fieldDescription : fieldList) {
1✔
324
                    fieldLoading[index] = new StackManipulation.Compound(
1✔
325
                            MethodVariableAccess.loadThis(),
1✔
326
                            MethodVariableAccess.load(instrumentedMethod.getParameters().get(index)),
1✔
327
                            FieldAccess.forField(fieldDescription).write()
1✔
328
                    );
329
                    index++;
1✔
330
                }
1✔
331
                StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
332
                        MethodVariableAccess.loadThis(),
1✔
333
                        MethodInvocation.invoke(ConstructorCall.INSTANCE.objectTypeDefaultConstructor),
1✔
334
                        new StackManipulation.Compound(fieldLoading),
335
                        MethodReturn.VOID
336
                ).apply(methodVisitor, implementationContext);
1✔
337
                return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
338
            }
339
        }
340
    }
341

342
    /**
343
     * An implementation for a method of a {@link net.bytebuddy.implementation.auxiliary.MethodCallProxy}.
344
     */
345
    @HashCodeAndEqualsPlugin.Enhance
346
    protected static class MethodCall implements Implementation {
347

348
        /**
349
         * The method that is accessed by the implemented method.
350
         */
351
        private final MethodDescription accessorMethod;
352

353
        /**
354
         * The assigner to be used for invoking the accessor method.
355
         */
356
        private final Assigner assigner;
357

358
        /**
359
         * Creates a new method call implementation.
360
         *
361
         * @param accessorMethod The method that is accessed by the implemented method.
362
         * @param assigner       The assigner to be used for invoking the accessor method.
363
         */
364
        protected MethodCall(MethodDescription accessorMethod, Assigner assigner) {
1✔
365
            this.accessorMethod = accessorMethod;
1✔
366
            this.assigner = assigner;
1✔
367
        }
1✔
368

369
        /**
370
         * {@inheritDoc}
371
         */
372
        public InstrumentedType prepare(InstrumentedType instrumentedType) {
373
            return instrumentedType;
1✔
374
        }
375

376
        /**
377
         * {@inheritDoc}
378
         */
379
        public ByteCodeAppender appender(Target implementationTarget) {
380
            return new Appender(implementationTarget.getInstrumentedType());
1✔
381
        }
382

383
        /**
384
         * The appender for implementing the {@link net.bytebuddy.implementation.auxiliary.MethodCallProxy.MethodCall}.
385
         */
386
        @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
387
        protected class Appender implements ByteCodeAppender {
388

389
            /**
390
             * The instrumented type that is implemented.
391
             */
392
            private final TypeDescription instrumentedType;
393

394
            /**
395
             * Creates a new appender.
396
             *
397
             * @param instrumentedType The instrumented type to be implemented.
398
             */
399
            private Appender(TypeDescription instrumentedType) {
1✔
400
                this.instrumentedType = instrumentedType;
1✔
401
            }
1✔
402

403
            /**
404
             * {@inheritDoc}
405
             */
406
            public Size apply(MethodVisitor methodVisitor,
407
                              Context implementationContext,
408
                              MethodDescription instrumentedMethod) {
409
                FieldList<?> fieldList = instrumentedType.getDeclaredFields();
1✔
410
                List<StackManipulation> fieldLoadings = new ArrayList<StackManipulation>(fieldList.size());
1✔
411
                for (FieldDescription fieldDescription : fieldList) {
1✔
412
                    fieldLoadings.add(new StackManipulation.Compound(MethodVariableAccess.loadThis(), FieldAccess.forField(fieldDescription).read()));
1✔
413
                }
1✔
414
                StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
415
                        new StackManipulation.Compound(fieldLoadings),
416
                        MethodInvocation.invoke(accessorMethod),
1✔
417
                        assigner.assign(accessorMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC),
1✔
418
                        MethodReturn.of(instrumentedMethod.getReturnType())
1✔
419
                ).apply(methodVisitor, implementationContext);
1✔
420
                return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
421
            }
422
        }
423
    }
424

425
    /**
426
     * A stack manipulation that creates a {@link net.bytebuddy.implementation.auxiliary.MethodCallProxy}
427
     * for a given method an pushes such an object onto the call stack. For this purpose, all arguments of the proxied method
428
     * are loaded onto the stack what is only possible if this instance is used from a method with an identical signature such
429
     * as the target method itself.
430
     */
431
    @HashCodeAndEqualsPlugin.Enhance
432
    public static class AssignableSignatureCall extends StackManipulation.AbstractBase {
433

434
        /**
435
         * The special method invocation to be proxied by this stack manipulation.
436
         */
437
        private final Implementation.SpecialMethodInvocation specialMethodInvocation;
438

439
        /**
440
         * Determines if the generated proxy should be serializableProxy.
441
         */
442
        private final boolean serializable;
443

444
        /**
445
         * Creates an operand stack assignment that creates a
446
         * {@link net.bytebuddy.implementation.auxiliary.MethodCallProxy} for the
447
         * {@code targetMethod} and pushes this proxy object onto the stack.
448
         *
449
         * @param specialMethodInvocation The special method invocation which should be invoked by the created method
450
         *                                call proxy.
451
         * @param serializable            Determines if the generated proxy should be serializableProxy.
452
         */
453
        public AssignableSignatureCall(Implementation.SpecialMethodInvocation specialMethodInvocation,
454
                                       boolean serializable) {
1✔
455
            this.specialMethodInvocation = specialMethodInvocation;
1✔
456
            this.serializable = serializable;
1✔
457
        }
1✔
458

459
        /**
460
         * {@inheritDoc}
461
         */
462
        public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
463
            TypeDescription auxiliaryType = implementationContext
1✔
464
                    .register(new MethodCallProxy(specialMethodInvocation, serializable));
1✔
465
            return new Compound(
1✔
466
                    TypeCreation.of(auxiliaryType),
1✔
467
                    Duplication.SINGLE,
468
                    MethodVariableAccess.allArgumentsOf(specialMethodInvocation.getMethodDescription()).prependThisReference(),
1✔
469
                    MethodInvocation.invoke(auxiliaryType.getDeclaredMethods().filter(isConstructor()).getOnly())
1✔
470
            ).apply(methodVisitor, implementationContext);
1✔
471
        }
472
    }
473
}
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