• 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

96.24
/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/Morph.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.annotation;
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.field.FieldDescription;
23
import net.bytebuddy.description.method.MethodDescription;
24
import net.bytebuddy.description.method.MethodList;
25
import net.bytebuddy.description.method.ParameterDescription;
26
import net.bytebuddy.description.type.TypeDescription;
27
import net.bytebuddy.dynamic.DynamicType;
28
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
29
import net.bytebuddy.dynamic.scaffold.TypeValidation;
30
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
31
import net.bytebuddy.implementation.Implementation;
32
import net.bytebuddy.implementation.MethodAccessorFactory;
33
import net.bytebuddy.implementation.auxiliary.AuxiliaryType;
34
import net.bytebuddy.implementation.bind.MethodDelegationBinder;
35
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
36
import net.bytebuddy.implementation.bytecode.Duplication;
37
import net.bytebuddy.implementation.bytecode.StackManipulation;
38
import net.bytebuddy.implementation.bytecode.TypeCreation;
39
import net.bytebuddy.implementation.bytecode.assign.Assigner;
40
import net.bytebuddy.implementation.bytecode.collection.ArrayAccess;
41
import net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
42
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
43
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
44
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
45
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
46
import net.bytebuddy.matcher.ElementMatchers;
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.lang.annotation.Documented;
53
import java.lang.annotation.ElementType;
54
import java.lang.annotation.Retention;
55
import java.lang.annotation.RetentionPolicy;
56
import java.lang.annotation.Target;
57
import java.util.Collections;
58

59
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
60
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
61
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
62
import static net.bytebuddy.matcher.ElementMatchers.named;
63

64
/**
65
 * This annotation instructs Byte Buddy to inject a proxy class that calls a method's super method with
66
 * explicit arguments. For this, the {@link Morph.Binder}
67
 * needs to be installed for an interface type that takes an argument of the array type {@link java.lang.Object} and
68
 * returns a non-array type of {@link java.lang.Object}. This is an alternative to using the
69
 * {@link net.bytebuddy.implementation.bind.annotation.SuperCall} or
70
 * {@link net.bytebuddy.implementation.bind.annotation.DefaultCall} annotations which call a super
71
 * method using the same arguments as the intercepted method was invoked with.
72
 *
73
 * @see net.bytebuddy.implementation.MethodDelegation
74
 * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
75
 */
76
@Documented
77
@Retention(RetentionPolicy.RUNTIME)
78
@Target(ElementType.PARAMETER)
79
public @interface Morph {
80

81
    /**
82
     * Determines if the injected proxy for this parameter should be serializable.
83
     *
84
     * @return {@code true} if the proxy should be serializable.
85
     */
86
    boolean serializableProxy() default false;
87

88
    /**
89
     * Determines if the proxy should attempt to invoke a default method. If the default method is ambiguous,
90
     * use the {@link Morph#defaultTarget()} property instead which allows to determine an explicit interface
91
     * on which the default method should be invoked on. If this other method is used, this property is ignored.
92
     *
93
     * @return {@code true} if a default method should be ignored.
94
     */
95
    boolean defaultMethod() default false;
96

97
    /**
98
     * The type on which a default method should be invoked. When this property is not set and the
99
     * {@link Morph#defaultMethod()} property is set to {@code false}, a normal super method invocation is attempted.
100
     *
101
     * @return The target interface of a default method call.
102
     */
103
    Class<?> defaultTarget() default void.class;
104

105
    /**
106
     * A binder for the {@link net.bytebuddy.implementation.bind.annotation.Morph} annotation.
107
     */
108
    @HashCodeAndEqualsPlugin.Enhance
109
    class Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<Morph> {
110

111
        /**
112
         * A reference to the serializable proxy method.
113
         */
114
        private static final MethodDescription.InDefinedShape SERIALIZABLE_PROXY;
115

116
        /**
117
         * A reference to the default method method.
118
         */
119
        private static final MethodDescription.InDefinedShape DEFAULT_METHOD;
120

121
        /**
122
         * A reference to the default target method.
123
         */
124
        private static final MethodDescription.InDefinedShape DEFAULT_TARGET;
125

126
        /*
127
         * Looks up references for all annotation properties of the morph annotation.
128
         */
129
        static {
130
            MethodList<MethodDescription.InDefinedShape> methodList = TypeDescription.ForLoadedType.of(Morph.class).getDeclaredMethods();
1✔
131
            SERIALIZABLE_PROXY = methodList.filter(named("serializableProxy")).getOnly();
1✔
132
            DEFAULT_METHOD = methodList.filter(named("defaultMethod")).getOnly();
1✔
133
            DEFAULT_TARGET = methodList.filter(named("defaultTarget")).getOnly();
1✔
134
        }
1✔
135

136
        /**
137
         * The method which is overridden for generating the proxy class.
138
         */
139
        private final MethodDescription forwardingMethod;
140

141
        /**
142
         * Creates a new binder.
143
         *
144
         * @param forwardingMethod The method which is overridden for generating the proxy class.
145
         */
146
        protected Binder(MethodDescription forwardingMethod) {
1✔
147
            this.forwardingMethod = forwardingMethod;
1✔
148
        }
1✔
149

150
        /**
151
         * Installs a given type for use on a {@link net.bytebuddy.implementation.bind.annotation.Morph}
152
         * annotation. The given type must be an interface without any super interfaces and a single method which
153
         * maps an {@link java.lang.Object} array to a {@link java.lang.Object} type. The use of generics is
154
         * permitted.
155
         *
156
         * @param type The type to install.
157
         * @return A binder for the {@link net.bytebuddy.implementation.bind.annotation.Morph}
158
         * annotation.
159
         */
160
        public static TargetMethodAnnotationDrivenBinder.ParameterBinder<Morph> install(Class<?> type) {
161
            return install(TypeDescription.ForLoadedType.of(type));
1✔
162
        }
163

164
        /**
165
         * Installs a given type for use on a {@link net.bytebuddy.implementation.bind.annotation.Morph}
166
         * annotation. The given type must be an interface without any super interfaces and a single method which
167
         * maps an {@link java.lang.Object} array to a {@link java.lang.Object} type. The use of generics is
168
         * permitted.
169
         *
170
         * @param typeDescription The type to install.
171
         * @return A binder for the {@link net.bytebuddy.implementation.bind.annotation.Morph}
172
         * annotation.
173
         */
174
        public static TargetMethodAnnotationDrivenBinder.ParameterBinder<Morph> install(TypeDescription typeDescription) {
175
            return new Binder(onlyMethod(typeDescription));
1✔
176
        }
177

178
        /**
179
         * Extracts the only method of a given type and validates to fit the constraints of the morph annotation.
180
         *
181
         * @param typeDescription The type to extract the method from.
182
         * @return The only method after validation.
183
         */
184
        private static MethodDescription onlyMethod(TypeDescription typeDescription) {
185
            if (!typeDescription.isInterface()) {
1✔
186
                throw new IllegalArgumentException(typeDescription + " is not an interface");
1✔
187
            } else if (!typeDescription.getInterfaces().isEmpty()) {
1✔
188
                throw new IllegalArgumentException(typeDescription + " must not extend other interfaces");
1✔
189
            } else if (!typeDescription.isPublic()) {
1✔
190
                throw new IllegalArgumentException(typeDescription + " is mot public");
1✔
191
            }
192
            MethodList<?> methodCandidates = typeDescription.getDeclaredMethods().filter(isAbstract());
1✔
193
            if (methodCandidates.size() != 1) {
1✔
194
                throw new IllegalArgumentException(typeDescription + " must declare exactly one abstract method");
1✔
195
            }
196
            MethodDescription methodDescription = methodCandidates.getOnly();
1✔
197
            if (!methodDescription.getReturnType().asErasure().represents(Object.class)) {
1✔
198
                throw new IllegalArgumentException(methodDescription + " does not return an Object-type");
×
199
            } else if (methodDescription.getParameters().size() != 1 || !methodDescription.getParameters().get(0).getType().asErasure().represents(Object[].class)) {
1✔
200
                throw new IllegalArgumentException(methodDescription + " does not take a single argument of type Object[]");
×
201
            }
202
            return methodDescription;
1✔
203
        }
204

205
        /**
206
         * {@inheritDoc}
207
         */
208
        public Class<Morph> getHandledType() {
209
            return Morph.class;
1✔
210
        }
211

212
        /**
213
         * {@inheritDoc}
214
         */
215
        public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<Morph> annotation,
216
                                                               MethodDescription source,
217
                                                               ParameterDescription target,
218
                                                               Implementation.Target implementationTarget,
219
                                                               Assigner assigner,
220
                                                               Assigner.Typing typing) {
221
            if (!target.getType().asErasure().equals(forwardingMethod.getDeclaringType())) {
1✔
222
                throw new IllegalStateException("Illegal use of @Morph for " + target + " which was installed for " + forwardingMethod.getDeclaringType());
1✔
223
            }
224
            Implementation.SpecialMethodInvocation specialMethodInvocation;
225
            TypeDescription typeDescription = annotation.getValue(DEFAULT_TARGET).resolve(TypeDescription.class);
1✔
226
            if (typeDescription.represents(void.class) && !annotation.getValue(DEFAULT_METHOD).resolve(Boolean.class)) {
1✔
227
                specialMethodInvocation = implementationTarget.invokeDominant(source.asSignatureToken()).withCheckedCompatibilityTo(source.asTypeToken());
1✔
228
            } else {
229
                specialMethodInvocation = (typeDescription.represents(void.class)
1✔
230
                        ? DefaultMethodLocator.Implicit.INSTANCE
231
                        : new DefaultMethodLocator.Explicit(typeDescription)).resolve(implementationTarget, source);
1✔
232
            }
233
            return specialMethodInvocation.isValid()
1✔
234
                    ? new MethodDelegationBinder.ParameterBinding.Anonymous(new RedirectionProxy(forwardingMethod.getDeclaringType().asErasure(),
1✔
235
                    implementationTarget.getInstrumentedType(),
1✔
236
                    specialMethodInvocation,
237
                    assigner,
238
                    annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class)))
1✔
239
                    : MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
240
        }
241

242
        /**
243
         * A default method locator is responsible for looking up a default method to a given source method.
244
         */
245
        protected interface DefaultMethodLocator {
246

247
            /**
248
             * Locates the correct default method to a given source method.
249
             *
250
             * @param implementationTarget The current implementation target.
251
             * @param source               The source method for which a default method should be looked up.
252
             * @return A special method invocation of the default method or an illegal special method invocation,
253
             * if no suitable invocation could be located.
254
             */
255
            Implementation.SpecialMethodInvocation resolve(Implementation.Target implementationTarget,
256
                                                           MethodDescription source);
257

258
            /**
259
             * An implicit default method locator that only permits the invocation of a default method if the source
260
             * method itself represents a method that was defined on a default method interface.
261
             */
262
            enum Implicit implements DefaultMethodLocator {
1✔
263

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

269
                /**
270
                 * {@inheritDoc}
271
                 */
272
                public Implementation.SpecialMethodInvocation resolve(Implementation.Target implementationTarget, MethodDescription source) {
273
                    return implementationTarget.invokeDefault(source.asSignatureToken()).withCheckedCompatibilityTo(source.asTypeToken());
1✔
274
                }
275
            }
276

277
            /**
278
             * An explicit default method locator attempts to look up a default method in the specified interface type.
279
             */
280
            @HashCodeAndEqualsPlugin.Enhance
281
            class Explicit implements DefaultMethodLocator {
282

283
                /**
284
                 * A description of the type on which the default method should be invoked.
285
                 */
286
                private final TypeDescription typeDescription;
287

288
                /**
289
                 * Creates a new explicit default method locator.
290
                 *
291
                 * @param typeDescription The actual target interface as explicitly defined by
292
                 *                        {@link DefaultCall#targetType()}.
293
                 */
294
                public Explicit(TypeDescription typeDescription) {
1✔
295
                    this.typeDescription = typeDescription;
1✔
296
                }
1✔
297

298
                /**
299
                 * {@inheritDoc}
300
                 */
301
                public Implementation.SpecialMethodInvocation resolve(Implementation.Target implementationTarget, MethodDescription source) {
302
                    if (!typeDescription.isInterface()) {
1✔
303
                        throw new IllegalStateException(source + " method carries default method call parameter on non-interface type");
×
304
                    }
305
                    return implementationTarget
1✔
306
                            .invokeDefault(source.asSignatureToken(), typeDescription)
1✔
307
                            .withCheckedCompatibilityTo(source.asTypeToken());
1✔
308
                }
309
            }
310
        }
311

312
        /**
313
         * A proxy that implements the installed interface in order to allow for a morphed super method invocation.
314
         */
315
        @HashCodeAndEqualsPlugin.Enhance
316
        protected static class RedirectionProxy extends StackManipulation.AbstractBase implements AuxiliaryType {
317

318
            /**
319
             * The name of the field that carries an instance for invoking a super method on.
320
             */
321
            protected static final String FIELD_NAME = "target";
322

323
            /**
324
             * The interface type that is implemented by the generated proxy.
325
             */
326
            private final TypeDescription morphingType;
327

328
            /**
329
             * The type that is instrumented on which the super method is invoked.
330
             */
331
            private final TypeDescription instrumentedType;
332

333
            /**
334
             * The special method invocation to be executed by the morphing type via an accessor on the
335
             * instrumented type.
336
             */
337
            private final Implementation.SpecialMethodInvocation specialMethodInvocation;
338

339
            /**
340
             * The assigner to use.
341
             */
342
            private final Assigner assigner;
343

344
            /**
345
             * Determines if the generated proxy should be {@link java.io.Serializable}.
346
             */
347
            private final boolean serializableProxy;
348

349
            /**
350
             * Creates a new redirection proxy.
351
             *
352
             * @param morphingType            The interface type that is implemented by the generated proxy.
353
             * @param instrumentedType        The type that is instrumented on which the super method is invoked.
354
             * @param specialMethodInvocation The special method invocation to be executed by the morphing type via
355
             *                                an accessor on the instrumented type.
356
             * @param assigner                The assigner to use.
357
             * @param serializableProxy       {@code true} if the proxy should be serializable.
358
             */
359
            protected RedirectionProxy(TypeDescription morphingType,
360
                                       TypeDescription instrumentedType,
361
                                       Implementation.SpecialMethodInvocation specialMethodInvocation,
362
                                       Assigner assigner,
363
                                       boolean serializableProxy) {
1✔
364
                this.morphingType = morphingType;
1✔
365
                this.instrumentedType = instrumentedType;
1✔
366
                this.specialMethodInvocation = specialMethodInvocation;
1✔
367
                this.assigner = assigner;
1✔
368
                this.serializableProxy = serializableProxy;
1✔
369
            }
1✔
370

371
            /**
372
             * {@inheritDoc}
373
             */
374
            public String getSuffix() {
375
                return RandomString.hashOf(morphingType.hashCode()) + (serializableProxy ? "S" : "0");
1✔
376
            }
377

378
            /**
379
             * {@inheritDoc}
380
             */
381
            public DynamicType make(String auxiliaryTypeName,
382
                                    ClassFileVersion classFileVersion,
383
                                    MethodAccessorFactory methodAccessorFactory) {
384
                return new ByteBuddy(classFileVersion)
1✔
385
                        .with(TypeValidation.DISABLED)
1✔
386
                        .subclass(morphingType, ConstructorStrategy.Default.NO_CONSTRUCTORS)
1✔
387
                        .name(auxiliaryTypeName)
1✔
388
                        .modifiers(DEFAULT_TYPE_MODIFIER)
1✔
389
                        .implement(serializableProxy ? new Class<?>[]{Serializable.class} : new Class<?>[0])
1✔
390
                        .defineConstructor().withParameters(specialMethodInvocation.getMethodDescription().isStatic()
1✔
391
                                ? Collections.<TypeDescription>emptyList()
1✔
392
                                : Collections.singletonList(instrumentedType))
1✔
393
                        .intercept(specialMethodInvocation.getMethodDescription().isStatic()
1✔
394
                                ? StaticFieldConstructor.INSTANCE
395
                                : new InstanceFieldConstructor(instrumentedType))
396
                        .method(ElementMatchers.<MethodDescription>isAbstract().and(isDeclaredBy(morphingType)))
1✔
397
                        .intercept(new MethodCall(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT), assigner))
1✔
398
                        .make();
1✔
399
            }
400

401
            /**
402
             * {@inheritDoc}
403
             */
404
            public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
405
                TypeDescription forwardingType = implementationContext.register(this);
1✔
406
                return new Compound(
1✔
407
                        TypeCreation.of(forwardingType),
1✔
408
                        Duplication.SINGLE,
409
                        specialMethodInvocation.getMethodDescription().isStatic()
1✔
410
                                ? Trivial.INSTANCE
411
                                : MethodVariableAccess.loadThis(),
1✔
412
                        MethodInvocation.invoke(forwardingType.getDeclaredMethods().filter(isConstructor()).getOnly())
1✔
413
                ).apply(methodVisitor, implementationContext);
1✔
414
            }
415

416
            /**
417
             * Creates an instance of the proxy when instrumenting a static method.
418
             */
419
            protected enum StaticFieldConstructor implements Implementation {
1✔
420

421
                /**
422
                 * The singleton instance.
423
                 */
424
                INSTANCE;
1✔
425

426
                /**
427
                 * A reference of the {@link Object} type default constructor.
428
                 */
429
                private final MethodDescription objectTypeDefaultConstructor;
430

431
                /**
432
                 * Creates the constructor call singleton.
433
                 */
434
                StaticFieldConstructor() {
1✔
435
                    objectTypeDefaultConstructor = TypeDescription.ForLoadedType.of(Object.class).getDeclaredMethods()
1✔
436
                            .filter(isConstructor())
1✔
437
                            .getOnly();
1✔
438
                }
1✔
439

440
                /**
441
                 * {@inheritDoc}
442
                 */
443
                public InstrumentedType prepare(InstrumentedType instrumentedType) {
444
                    return instrumentedType;
×
445
                }
446

447
                /**
448
                 * {@inheritDoc}
449
                 */
450
                public ByteCodeAppender appender(Target implementationTarget) {
451
                    return new ByteCodeAppender.Simple(MethodVariableAccess.loadThis(), MethodInvocation.invoke(objectTypeDefaultConstructor), MethodReturn.VOID);
×
452
                }
453
            }
454

455
            /**
456
             * Creates an instance of the proxy when instrumenting an instance method.
457
             */
458
            @HashCodeAndEqualsPlugin.Enhance
459
            protected static class InstanceFieldConstructor implements Implementation {
460

461
                /**
462
                 * The instrumented type.
463
                 */
464
                private final TypeDescription instrumentedType;
465

466
                /**
467
                 * Creates a new instance field constructor implementation.
468
                 *
469
                 * @param instrumentedType The instrumented type.
470
                 */
471
                protected InstanceFieldConstructor(TypeDescription instrumentedType) {
1✔
472
                    this.instrumentedType = instrumentedType;
1✔
473
                }
1✔
474

475
                /**
476
                 * {@inheritDoc}
477
                 */
478
                public InstrumentedType prepare(InstrumentedType instrumentedType) {
479
                    return instrumentedType.withField(new FieldDescription.Token(RedirectionProxy.FIELD_NAME,
1✔
480
                            Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE,
481
                            this.instrumentedType.asGenericType()));
1✔
482
                }
483

484
                /**
485
                 * {@inheritDoc}
486
                 */
487
                public ByteCodeAppender appender(Target implementationTarget) {
488
                    return new Appender(implementationTarget);
1✔
489
                }
490

491
                /**
492
                 * The byte code appender that implements the constructor.
493
                 */
494
                @HashCodeAndEqualsPlugin.Enhance
495
                protected static class Appender implements ByteCodeAppender {
496

497
                    /**
498
                     * The field that carries the instance on which the super method is invoked.
499
                     */
500
                    private final FieldDescription fieldDescription;
501

502
                    /**
503
                     * Creates a new appender.
504
                     *
505
                     * @param implementationTarget The current implementation target.
506
                     */
507
                    protected Appender(Target implementationTarget) {
1✔
508
                        fieldDescription = implementationTarget.getInstrumentedType()
1✔
509
                                .getDeclaredFields()
1✔
510
                                .filter((named(RedirectionProxy.FIELD_NAME)))
1✔
511
                                .getOnly();
1✔
512
                    }
1✔
513

514
                    /**
515
                     * {@inheritDoc}
516
                     */
517
                    public Size apply(MethodVisitor methodVisitor,
518
                                      Context implementationContext,
519
                                      MethodDescription instrumentedMethod) {
520
                        StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
521
                                MethodVariableAccess.loadThis(),
1✔
522
                                MethodInvocation.invoke(StaticFieldConstructor.INSTANCE.objectTypeDefaultConstructor),
1✔
523
                                MethodVariableAccess.allArgumentsOf(instrumentedMethod).prependThisReference(),
1✔
524
                                FieldAccess.forField(fieldDescription).write(),
1✔
525
                                MethodReturn.VOID
526
                        ).apply(methodVisitor, implementationContext);
1✔
527
                        return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
528
                    }
529
                }
530
            }
531

532
            /**
533
             * Implements a the method call of the morphing method.
534
             */
535
            @HashCodeAndEqualsPlugin.Enhance
536
            protected static class MethodCall implements Implementation {
537

538
                /**
539
                 * The accessor method to invoke from the proxy's method.
540
                 */
541
                private final MethodDescription accessorMethod;
542

543
                /**
544
                 * The assigner to be used.
545
                 */
546
                private final Assigner assigner;
547

548
                /**
549
                 * Creates a new method call implementation for a proxy method.
550
                 *
551
                 * @param accessorMethod The accessor method to invoke from the proxy's method.
552
                 * @param assigner       The assigner to be used.
553
                 */
554
                protected MethodCall(MethodDescription accessorMethod, Assigner assigner) {
1✔
555
                    this.accessorMethod = accessorMethod;
1✔
556
                    this.assigner = assigner;
1✔
557
                }
1✔
558

559
                /**
560
                 * {@inheritDoc}
561
                 */
562
                public InstrumentedType prepare(InstrumentedType instrumentedType) {
563
                    return instrumentedType;
1✔
564
                }
565

566
                /**
567
                 * {@inheritDoc}
568
                 */
569
                public ByteCodeAppender appender(Target implementationTarget) {
570
                    return new Appender(implementationTarget);
1✔
571
                }
572

573
                /**
574
                 * The byte code appender to implement the method.
575
                 */
576
                @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
577
                protected class Appender implements ByteCodeAppender {
578

579
                    /**
580
                     * The proxy type.
581
                     */
582
                    private final TypeDescription typeDescription;
583

584
                    /**
585
                     * Creates a new appender.
586
                     *
587
                     * @param implementationTarget The current implementation target.
588
                     */
589
                    protected Appender(Target implementationTarget) {
1✔
590
                        typeDescription = implementationTarget.getInstrumentedType();
1✔
591
                    }
1✔
592

593
                    /**
594
                     * {@inheritDoc}
595
                     */
596
                    public Size apply(MethodVisitor methodVisitor,
597
                                      Context implementationContext,
598
                                      MethodDescription instrumentedMethod) {
599
                        StackManipulation arrayReference = MethodVariableAccess.REFERENCE.loadFrom(1);
1✔
600
                        StackManipulation[] parameterLoading = new StackManipulation[accessorMethod.getParameters().size()];
1✔
601
                        int index = 0;
1✔
602
                        for (TypeDescription.Generic parameterType : accessorMethod.getParameters().asTypeList()) {
1✔
603
                            parameterLoading[index] = new StackManipulation.Compound(arrayReference,
1✔
604
                                    IntegerConstant.forValue(index),
1✔
605
                                    ArrayAccess.REFERENCE.load(),
1✔
606
                                    assigner.assign(TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(Object.class), parameterType, Assigner.Typing.DYNAMIC));
1✔
607
                            index++;
1✔
608
                        }
1✔
609
                        StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
610
                                accessorMethod.isStatic()
1✔
611
                                        ? Trivial.INSTANCE
612
                                        : new StackManipulation.Compound(
613
                                        MethodVariableAccess.loadThis(),
1✔
614
                                        FieldAccess.forField(typeDescription.getDeclaredFields()
1✔
615
                                                .filter((named(RedirectionProxy.FIELD_NAME)))
1✔
616
                                                .getOnly()).read()),
1✔
617
                                new StackManipulation.Compound(parameterLoading),
618
                                MethodInvocation.invoke(accessorMethod),
1✔
619
                                assigner.assign(accessorMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC),
1✔
620
                                MethodReturn.REFERENCE
621
                        ).apply(methodVisitor, implementationContext);
1✔
622
                        return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
623
                    }
624
                }
625
            }
626
        }
627
    }
628
}
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