• 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

98.49
/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/FieldProxy.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.ExceptionMethod;
32
import net.bytebuddy.implementation.Implementation;
33
import net.bytebuddy.implementation.MethodAccessorFactory;
34
import net.bytebuddy.implementation.auxiliary.AuxiliaryType;
35
import net.bytebuddy.implementation.bind.MethodDelegationBinder;
36
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
37
import net.bytebuddy.implementation.bytecode.Duplication;
38
import net.bytebuddy.implementation.bytecode.StackManipulation;
39
import net.bytebuddy.implementation.bytecode.TypeCreation;
40
import net.bytebuddy.implementation.bytecode.assign.Assigner;
41
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
42
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
43
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
44
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
45
import net.bytebuddy.utility.RandomString;
46
import org.objectweb.asm.MethodVisitor;
47
import org.objectweb.asm.Opcodes;
48

49
import java.io.Serializable;
50
import java.lang.annotation.Documented;
51
import java.lang.annotation.ElementType;
52
import java.lang.annotation.Retention;
53
import java.lang.annotation.RetentionPolicy;
54
import java.lang.annotation.Target;
55
import java.util.Collections;
56

57
import static net.bytebuddy.matcher.ElementMatchers.definedMethod;
58
import static net.bytebuddy.matcher.ElementMatchers.is;
59
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
60
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
61
import static net.bytebuddy.matcher.ElementMatchers.isGetter;
62
import static net.bytebuddy.matcher.ElementMatchers.isSetter;
63
import static net.bytebuddy.matcher.ElementMatchers.named;
64

65
/**
66
 * Using this annotation it is possible to access fields by getter and setter types. Before this annotation can be
67
 * used, it needs to be installed with two types. The getter type must be defined in a single-method interface
68
 * with a single method that returns an {@link java.lang.Object} type and takes no arguments. The setter interface
69
 * must similarly return {@code void} and take a single {@link java.lang.Object} argument. After installing these
70
 * interfaces with the {@link FieldProxy.Binder}, this
71
 * binder needs to be registered with a {@link net.bytebuddy.implementation.MethodDelegation} before it can be used.
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 FieldProxy {
80

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

88
    /**
89
     * Determines the name of the field that is to be accessed. If this property is not set, a field name is inferred
90
     * by the intercepted method after the Java beans naming conventions.
91
     *
92
     * @return The name of the field to be accessed.
93
     */
94
    String value() default TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFieldBinding.BEAN_PROPERTY;
95

96
    /**
97
     * Determines which type defines the field that is to be accessed. If this property is not set, the most field
98
     * that is defined highest in the type hierarchy is accessed.
99
     *
100
     * @return The type that defines the accessed field.
101
     */
102
    Class<?> declaringType() default void.class;
103

104
    /**
105
     * A binder for the {@link FieldProxy} annotation.
106
     */
107
    @HashCodeAndEqualsPlugin.Enhance
108
    class Binder extends TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFieldBinding<FieldProxy> {
109

110
        /**
111
         * A reference to the method that declares the field annotation's defining type property.
112
         */
113
        private static final MethodDescription.InDefinedShape DECLARING_TYPE;
114

115
        /**
116
         * A reference to the method that declares the field annotation's field name property.
117
         */
118
        private static final MethodDescription.InDefinedShape FIELD_NAME;
119

120
        /**
121
         * A reference to the method that declares the field annotation's serializable proxy property.
122
         */
123
        private static final MethodDescription.InDefinedShape SERIALIZABLE_PROXY;
124

125
        /*
126
         * Fetches a reference to all annotation properties.
127
         */
128
        static {
129
            MethodList<MethodDescription.InDefinedShape> methodList = TypeDescription.ForLoadedType.of(FieldProxy.class).getDeclaredMethods();
1✔
130
            DECLARING_TYPE = methodList.filter(named("declaringType")).getOnly();
1✔
131
            FIELD_NAME = methodList.filter(named("value")).getOnly();
1✔
132
            SERIALIZABLE_PROXY = methodList.filter(named("serializableProxy")).getOnly();
1✔
133
        }
1✔
134

135
        /**
136
         * Creates a binder by installing a single proxy type where annotating a parameter with {@link FieldProxy} allows
137
         * getting and setting values for a given field.
138
         *
139
         * @param type A type which declares exactly one abstract getter and an abstract setter for the {@link Object}
140
         *             type. The type is allowed to be generic.
141
         * @return A binder for the {@link FieldProxy} annotation.
142
         */
143
        public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(Class<?> type) {
144
            return install(TypeDescription.ForLoadedType.of(type));
1✔
145
        }
146

147
        /**
148
         * Creates a binder by installing a single proxy type where annotating a parameter with {@link FieldProxy} allows
149
         * getting and setting values for a given field.
150
         *
151
         * @param typeDescription A type which declares exactly one abstract getter and an abstract setter for the {@link Object}
152
         *                        type. The type is allowed to be generic.
153
         * @return A binder for the {@link FieldProxy} annotation.
154
         */
155
        public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(TypeDescription typeDescription) {
156
            if (!typeDescription.isInterface()) {
1✔
157
                throw new IllegalArgumentException(typeDescription + " is not an interface");
1✔
158
            } else if (!typeDescription.getInterfaces().isEmpty()) {
1✔
159
                throw new IllegalArgumentException(typeDescription + " must not extend other interfaces");
1✔
160
            } else if (!typeDescription.isPublic()) {
1✔
161
                throw new IllegalArgumentException(typeDescription + " is not public");
1✔
162
            }
163
            MethodList<MethodDescription.InDefinedShape> methodCandidates = typeDescription.getDeclaredMethods().filter(isAbstract());
1✔
164
            if (methodCandidates.size() != 2) {
1✔
165
                throw new IllegalArgumentException(typeDescription + " does not declare exactly two non-abstract methods");
1✔
166
            }
167
            MethodList<MethodDescription.InDefinedShape> getterCandidates = methodCandidates.filter(isGetter(Object.class));
1✔
168
            if (getterCandidates.size() != 1) {
1✔
169
                throw new IllegalArgumentException(typeDescription + " does not declare a getter with an Object type");
1✔
170
            }
171
            MethodList<MethodDescription.InDefinedShape> setterCandidates = methodCandidates.filter(isSetter(Object.class));
1✔
172
            if (setterCandidates.size() != 1) {
1✔
173
                throw new IllegalArgumentException(typeDescription + " does not declare a setter with an Object type");
1✔
174
            }
175
            return new Binder(typeDescription, getterCandidates.getOnly(), setterCandidates.getOnly());
1✔
176
        }
177

178
        /**
179
         * Creates a binder by installing two proxy types which are implemented by this binder if a field getter
180
         * or a field setter is requested by using the
181
         * {@link FieldProxy} annotation.
182
         *
183
         * @param getterType The type which should be used for getter proxies. The type must
184
         *                   represent an interface which defines a single method which returns an
185
         *                   {@link java.lang.Object} return type and does not take any arguments. The use of generics
186
         *                   is permitted.
187
         * @param setterType The type which should be uses for setter proxies. The type must
188
         *                   represent an interface which defines a single method which returns {@code void}
189
         *                   and takes a single {@link java.lang.Object}-typed argument. The use of generics
190
         *                   is permitted.
191
         * @return A binder for the {@link FieldProxy} annotation.
192
         */
193
        public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(Class<?> getterType, Class<?> setterType) {
194
            return install(TypeDescription.ForLoadedType.of(getterType), TypeDescription.ForLoadedType.of(setterType));
1✔
195
        }
196

197
        /**
198
         * Creates a binder by installing two proxy types which are implemented by this binder if a field getter
199
         * or a field setter is requested by using the
200
         * {@link FieldProxy} annotation.
201
         *
202
         * @param getterType The type which should be used for getter proxies. The type must
203
         *                   represent an interface which defines a single method which returns an
204
         *                   {@link java.lang.Object} return type and does not take any arguments. The use of generics
205
         *                   is permitted.
206
         * @param setterType The type which should be uses for setter proxies. The type must
207
         *                   represent an interface which defines a single method which returns {@code void}
208
         *                   and takes a single {@link java.lang.Object}-typed argument. The use of generics
209
         *                   is permitted.
210
         * @return A binder for the {@link FieldProxy} annotation.
211
         */
212
        public static TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> install(TypeDescription getterType, TypeDescription setterType) {
213
            MethodDescription.InDefinedShape getterMethod = onlyMethod(getterType);
1✔
214
            if (!getterMethod.getReturnType().asErasure().represents(Object.class)) {
1✔
215
                throw new IllegalArgumentException(getterMethod + " must take a single Object-typed parameter");
×
216
            } else if (getterMethod.getParameters().size() != 0) {
1✔
217
                throw new IllegalArgumentException(getterMethod + " must not declare parameters");
1✔
218
            }
219
            MethodDescription.InDefinedShape setterMethod = onlyMethod(setterType);
1✔
220
            if (!setterMethod.getReturnType().asErasure().represents(void.class)) {
1✔
221
                throw new IllegalArgumentException(setterMethod + " must return void");
1✔
222
            } else if (setterMethod.getParameters().size() != 1 || !setterMethod.getParameters().get(0).getType().asErasure().represents(Object.class)) {
1✔
223
                throw new IllegalArgumentException(setterMethod + " must declare a single Object-typed parameters");
×
224
            }
225
            return new Binder(getterMethod, setterMethod);
1✔
226
        }
227

228
        /**
229
         * Extracts the only method from a given type description which is validated for the required properties for
230
         * using the type as a proxy base type.
231
         *
232
         * @param typeDescription The type description to evaluate.
233
         * @return The only method which was found to be compatible to the proxy requirements.
234
         */
235
        private static MethodDescription.InDefinedShape onlyMethod(TypeDescription typeDescription) {
236
            if (!typeDescription.isInterface()) {
1✔
237
                throw new IllegalArgumentException(typeDescription + " is not an interface");
1✔
238
            } else if (!typeDescription.getInterfaces().isEmpty()) {
1✔
239
                throw new IllegalArgumentException(typeDescription + " must not extend other interfaces");
1✔
240
            } else if (!typeDescription.isPublic()) {
1✔
241
                throw new IllegalArgumentException(typeDescription + " is not public");
1✔
242
            }
243
            MethodList<MethodDescription.InDefinedShape> methodCandidates = typeDescription.getDeclaredMethods().filter(isAbstract());
1✔
244
            if (methodCandidates.size() != 1) {
1✔
245
                throw new IllegalArgumentException(typeDescription + " must declare exactly one abstract method");
1✔
246
            }
247
            return methodCandidates.getOnly();
1✔
248
        }
249

250
        /**
251
         * The field resolver factory to apply by this binder.
252
         */
253
        private final FieldResolver.Factory fieldResolverFactory;
254

255
        /**
256
         * Creates a new binder for a {@link FieldProxy} in simplex mode.
257
         *
258
         * @param getterMethod The getter method.
259
         * @param setterMethod The setter method.
260
         */
261
        protected Binder(MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) {
262
            this(new FieldResolver.Factory.Simplex(getterMethod, setterMethod));
1✔
263
        }
1✔
264

265
        /**
266
         * Creates a new binder for a {@link FieldProxy} in duplex mode.
267
         *
268
         * @param proxyType    The proxy type.
269
         * @param getterMethod The getter method.
270
         * @param setterMethod The setter method.
271
         */
272
        protected Binder(TypeDescription proxyType, MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) {
273
            this(new FieldResolver.Factory.Duplex(proxyType, getterMethod, setterMethod));
1✔
274
        }
1✔
275

276
        /**
277
         * Creates a new binder for a {@link FieldProxy}.
278
         *
279
         * @param fieldResolverFactory The field resolver factory to apply by this binder.
280
         */
281
        protected Binder(FieldResolver.Factory fieldResolverFactory) {
1✔
282
            this.fieldResolverFactory = fieldResolverFactory;
1✔
283
        }
1✔
284

285
        /**
286
         * {@inheritDoc}
287
         */
288
        public Class<FieldProxy> getHandledType() {
289
            return FieldProxy.class;
1✔
290
        }
291

292
        @Override
293
        protected String fieldName(AnnotationDescription.Loadable<FieldProxy> annotation) {
294
            return annotation.getValue(FIELD_NAME).resolve(String.class);
1✔
295
        }
296

297
        @Override
298
        protected TypeDescription declaringType(AnnotationDescription.Loadable<FieldProxy> annotation) {
299
            return annotation.getValue(DECLARING_TYPE).resolve(TypeDescription.class);
1✔
300
        }
301

302
        @Override
303
        protected MethodDelegationBinder.ParameterBinding<?> bind(FieldDescription fieldDescription,
304
                                                                  AnnotationDescription.Loadable<FieldProxy> annotation,
305
                                                                  MethodDescription source,
306
                                                                  ParameterDescription target,
307
                                                                  Implementation.Target implementationTarget,
308
                                                                  Assigner assigner) {
309
            FieldResolver fieldResolver = fieldResolverFactory.resolve(target.getType().asErasure(), fieldDescription);
1✔
310
            if (fieldResolver.isResolved()) {
1✔
311
                return new MethodDelegationBinder.ParameterBinding.Anonymous(new AccessorProxy(fieldDescription,
1✔
312
                        implementationTarget.getInstrumentedType(),
1✔
313
                        fieldResolver,
314
                        assigner,
315
                        annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class)));
1✔
316
            } else {
317
                return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
1✔
318
            }
319
        }
320

321
        /**
322
         * A resolver for creating an instrumentation for a field access.
323
         */
324
        protected interface FieldResolver {
325

326
            /**
327
             * Returns {@code true} if the field access can be established.
328
             *
329
             * @return {@code true} if the field access can be established.
330
             */
331
            boolean isResolved();
332

333
            /**
334
             * Returns the type of the field access proxy.
335
             *
336
             * @return The type of the field access proxy.
337
             */
338
            TypeDescription getProxyType();
339

340
            /**
341
             * Applies this field resolver to a dynamic type.
342
             *
343
             * @param builder               The dynamic type builder to use.
344
             * @param fieldDescription      The accessed field.
345
             * @param assigner              The assigner to use.
346
             * @param methodAccessorFactory The method accessor factory to use.
347
             * @return The builder for creating the field accessor proxy type.
348
             */
349
            DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
350
                                         FieldDescription fieldDescription,
351
                                         Assigner assigner,
352
                                         MethodAccessorFactory methodAccessorFactory);
353

354
            /**
355
             * A factory for creating a field resolver.
356
             */
357
            interface Factory {
358

359
                /**
360
                 * Creates a field resolver.
361
                 *
362
                 * @param parameterType    The type of the annotated parameter.
363
                 * @param fieldDescription The field being proxied.
364
                 * @return An appropriate field resolver.
365
                 */
366
                FieldResolver resolve(TypeDescription parameterType, FieldDescription fieldDescription);
367

368
                /**
369
                 * A duplex factory for a type that both sets and gets a field value.
370
                 */
371
                @HashCodeAndEqualsPlugin.Enhance
372
                class Duplex implements Factory {
373

374
                    /**
375
                     * The type of the accessor proxy.
376
                     */
377
                    private final TypeDescription proxyType;
378

379
                    /**
380
                     * The getter method.
381
                     */
382
                    private final MethodDescription.InDefinedShape getterMethod;
383

384
                    /**
385
                     * The setter method.
386
                     */
387
                    private final MethodDescription.InDefinedShape setterMethod;
388

389
                    /**
390
                     * Creates a new duplex factory.
391
                     *
392
                     * @param proxyType    The type of the accessor proxy.
393
                     * @param getterMethod The getter method.
394
                     * @param setterMethod The setter method.
395
                     */
396
                    protected Duplex(TypeDescription proxyType,
397
                                     MethodDescription.InDefinedShape getterMethod,
398
                                     MethodDescription.InDefinedShape setterMethod) {
1✔
399
                        this.proxyType = proxyType;
1✔
400
                        this.getterMethod = getterMethod;
1✔
401
                        this.setterMethod = setterMethod;
1✔
402
                    }
1✔
403

404
                    /**
405
                     * {@inheritDoc}
406
                     */
407
                    public FieldResolver resolve(TypeDescription parameterType, FieldDescription fieldDescription) {
408
                        if (parameterType.equals(proxyType)) {
1✔
409
                            return new ForGetterSetterPair(proxyType, getterMethod, setterMethod);
1✔
410
                        } else {
411
                            throw new IllegalStateException("Cannot use @FieldProxy on a non-installed type");
×
412
                        }
413
                    }
414
                }
415

416
                /**
417
                 * A simplex factory where field getters and setters both have their own type.
418
                 */
419
                @HashCodeAndEqualsPlugin.Enhance
420
                class Simplex implements Factory {
421

422
                    /**
423
                     * The getter method.
424
                     */
425
                    private final MethodDescription.InDefinedShape getterMethod;
426

427
                    /**
428
                     * The setter method.
429
                     */
430
                    private final MethodDescription.InDefinedShape setterMethod;
431

432
                    /**
433
                     * Creates a simplex factory.
434
                     *
435
                     * @param getterMethod The getter method.
436
                     * @param setterMethod The setter method.
437
                     */
438
                    protected Simplex(MethodDescription.InDefinedShape getterMethod, MethodDescription.InDefinedShape setterMethod) {
1✔
439
                        this.getterMethod = getterMethod;
1✔
440
                        this.setterMethod = setterMethod;
1✔
441
                    }
1✔
442

443
                    /**
444
                     * {@inheritDoc}
445
                     */
446
                    public FieldResolver resolve(TypeDescription parameterType, FieldDescription fieldDescription) {
447
                        if (parameterType.equals(getterMethod.getDeclaringType())) {
1✔
448
                            return new ForGetter(getterMethod);
1✔
449
                        } else if (parameterType.equals(setterMethod.getDeclaringType())) {
1✔
450
                            return fieldDescription.isFinal()
1✔
451
                                    ? Unresolved.INSTANCE
452
                                    : new ForSetter(setterMethod);
453
                        } else {
454
                            throw new IllegalStateException("Cannot use @FieldProxy on a non-installed type");
1✔
455
                        }
456
                    }
457
                }
458
            }
459

460
            /**
461
             * An unresolved field resolver.
462
             */
463
            enum Unresolved implements FieldResolver {
1✔
464

465
                /**
466
                 * The singleton instance.
467
                 */
468
                INSTANCE;
1✔
469

470
                /**
471
                 * {@inheritDoc}
472
                 */
473
                public boolean isResolved() {
474
                    return false;
1✔
475
                }
476

477
                /**
478
                 * {@inheritDoc}
479
                 */
480
                public TypeDescription getProxyType() {
481
                    throw new IllegalStateException("Cannot read type for unresolved field resolver");
1✔
482
                }
483

484
                /**
485
                 * {@inheritDoc}
486
                 */
487
                public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
488
                                                    FieldDescription fieldDescription,
489
                                                    Assigner assigner,
490
                                                    MethodAccessorFactory methodAccessorFactory) {
491
                    throw new IllegalStateException("Cannot apply unresolved field resolver");
1✔
492
                }
493
            }
494

495
            /**
496
             * A field resolver for a getter accessor.
497
             */
498
            @HashCodeAndEqualsPlugin.Enhance
499
            class ForGetter implements FieldResolver {
500

501
                /**
502
                 * The getter method.
503
                 */
504
                private final MethodDescription.InDefinedShape getterMethod;
505

506
                /**
507
                 * Creates a new getter field resolver.
508
                 *
509
                 * @param getterMethod The getter method.
510
                 */
511
                protected ForGetter(MethodDescription.InDefinedShape getterMethod) {
1✔
512
                    this.getterMethod = getterMethod;
1✔
513
                }
1✔
514

515
                /**
516
                 * {@inheritDoc}
517
                 */
518
                public boolean isResolved() {
519
                    return true;
1✔
520
                }
521

522
                /**
523
                 * {@inheritDoc}
524
                 */
525
                public TypeDescription getProxyType() {
526
                    return getterMethod.getDeclaringType();
1✔
527
                }
528

529
                /**
530
                 * {@inheritDoc}
531
                 */
532
                public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
533
                                                    FieldDescription fieldDescription,
534
                                                    Assigner assigner,
535
                                                    MethodAccessorFactory methodAccessorFactory) {
536
                    return builder.method(definedMethod(is(getterMethod))).intercept(new FieldGetter(fieldDescription, assigner, methodAccessorFactory));
1✔
537
                }
538
            }
539

540
            /**
541
             * A field resolver for a setter accessor.
542
             */
543
            @HashCodeAndEqualsPlugin.Enhance
544
            class ForSetter implements FieldResolver {
545

546
                /**
547
                 * The setter method.
548
                 */
549
                private final MethodDescription.InDefinedShape setterMethod;
550

551
                /**
552
                 * Creates a new field resolver for a setter accessor.
553
                 *
554
                 * @param setterMethod The setter method.
555
                 */
556
                protected ForSetter(MethodDescription.InDefinedShape setterMethod) {
1✔
557
                    this.setterMethod = setterMethod;
1✔
558
                }
1✔
559

560
                /**
561
                 * {@inheritDoc}
562
                 */
563
                public boolean isResolved() {
564
                    return true;
1✔
565
                }
566

567
                /**
568
                 * {@inheritDoc}
569
                 */
570
                public TypeDescription getProxyType() {
571
                    return setterMethod.getDeclaringType();
1✔
572
                }
573

574
                /**
575
                 * {@inheritDoc}
576
                 */
577
                public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
578
                                                    FieldDescription fieldDescription,
579
                                                    Assigner assigner,
580
                                                    MethodAccessorFactory methodAccessorFactory) {
581
                    return builder.method(is(setterMethod)).intercept(new FieldSetter(fieldDescription, assigner, methodAccessorFactory));
1✔
582
                }
583
            }
584

585
            /**
586
             * A field resolver for an accessor that both gets and sets a field value.
587
             */
588
            @HashCodeAndEqualsPlugin.Enhance
589
            class ForGetterSetterPair implements FieldResolver {
590

591
                /**
592
                 * The type of the accessor proxy.
593
                 */
594
                private final TypeDescription proxyType;
595

596
                /**
597
                 * The getter method.
598
                 */
599
                private final MethodDescription.InDefinedShape getterMethod;
600

601
                /**
602
                 * The setter method.
603
                 */
604
                private final MethodDescription.InDefinedShape setterMethod;
605

606
                /**
607
                 * Creates a new field resolver for an accessor that both gets and sets a field value.
608
                 *
609
                 * @param proxyType    The type of the accessor proxy.
610
                 * @param getterMethod The getter method.
611
                 * @param setterMethod The setter method.
612
                 */
613
                protected ForGetterSetterPair(TypeDescription proxyType,
614
                                              MethodDescription.InDefinedShape getterMethod,
615
                                              MethodDescription.InDefinedShape setterMethod) {
1✔
616
                    this.proxyType = proxyType;
1✔
617
                    this.getterMethod = getterMethod;
1✔
618
                    this.setterMethod = setterMethod;
1✔
619
                }
1✔
620

621
                /**
622
                 * {@inheritDoc}
623
                 */
624
                public boolean isResolved() {
625
                    return true;
1✔
626
                }
627

628
                /**
629
                 * {@inheritDoc}
630
                 */
631
                public TypeDescription getProxyType() {
632
                    return proxyType;
1✔
633
                }
634

635
                /**
636
                 * {@inheritDoc}
637
                 */
638
                public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
639
                                                    FieldDescription fieldDescription,
640
                                                    Assigner assigner,
641
                                                    MethodAccessorFactory methodAccessorFactory) {
642
                    return builder
1✔
643
                            .method(is(getterMethod)).intercept(new FieldGetter(fieldDescription, assigner, methodAccessorFactory))
1✔
644
                            .method(is(setterMethod)).intercept(fieldDescription.isFinal()
1✔
645
                                    ? ExceptionMethod.throwing(UnsupportedOperationException.class, "Cannot set final field " + fieldDescription)
1✔
646
                                    : new FieldSetter(fieldDescription, assigner, methodAccessorFactory));
647
                }
648
            }
649
        }
650

651
        /**
652
         * Represents an implementation for implementing a proxy type constructor when a static field is accessed.
653
         */
654
        protected enum StaticFieldConstructor implements Implementation {
1✔
655

656
            /**
657
             * The singleton instance.
658
             */
659
            INSTANCE;
1✔
660

661
            /**
662
             * A reference of the {@link Object} type default constructor.
663
             */
664
            private final MethodDescription objectTypeDefaultConstructor;
665

666
            /**
667
             * Creates the constructor call singleton.
668
             */
669
            StaticFieldConstructor() {
1✔
670
                objectTypeDefaultConstructor = TypeDescription.ForLoadedType.of(Object.class).getDeclaredMethods().filter(isConstructor()).getOnly();
1✔
671
            }
1✔
672

673
            /**
674
             * {@inheritDoc}
675
             */
676
            public InstrumentedType prepare(InstrumentedType instrumentedType) {
677
                return instrumentedType;
1✔
678
            }
679

680
            /**
681
             * {@inheritDoc}
682
             */
683
            public ByteCodeAppender appender(Target implementationTarget) {
684
                return new ByteCodeAppender.Simple(MethodVariableAccess.loadThis(), MethodInvocation.invoke(objectTypeDefaultConstructor), MethodReturn.VOID);
1✔
685
            }
686
        }
687

688
        /**
689
         * Represents an implementation for implementing a proxy type constructor when a non-static field is accessed.
690
         */
691
        @HashCodeAndEqualsPlugin.Enhance
692
        protected static class InstanceFieldConstructor implements Implementation {
693

694
            /**
695
             * The instrumented type from which a field is to be accessed.
696
             */
697
            private final TypeDescription instrumentedType;
698

699
            /**
700
             * Creates a new implementation for implementing a field accessor proxy's constructor when accessing
701
             * a non-static field.
702
             *
703
             * @param instrumentedType The instrumented type from which a field is to be accessed.
704
             */
705
            protected InstanceFieldConstructor(TypeDescription instrumentedType) {
1✔
706
                this.instrumentedType = instrumentedType;
1✔
707
            }
1✔
708

709
            /**
710
             * {@inheritDoc}
711
             */
712
            public InstrumentedType prepare(InstrumentedType instrumentedType) {
713
                return instrumentedType.withField(new FieldDescription.Token(AccessorProxy.FIELD_NAME,
1✔
714
                        Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE,
715
                        this.instrumentedType.asGenericType()));
1✔
716
            }
717

718
            /**
719
             * {@inheritDoc}
720
             */
721
            public ByteCodeAppender appender(Target implementationTarget) {
722
                return new Appender(implementationTarget);
1✔
723
            }
724

725
            /**
726
             * An appender for implementing an
727
             * {@link FieldProxy.Binder.InstanceFieldConstructor}.
728
             */
729
            @HashCodeAndEqualsPlugin.Enhance
730
            protected static class Appender implements ByteCodeAppender {
731

732
                /**
733
                 * The field to be set within the constructor.
734
                 */
735
                private final FieldDescription fieldDescription;
736

737
                /**
738
                 * Creates a new appender.
739
                 *
740
                 * @param implementationTarget The implementation target of the current implementation.
741
                 */
742
                protected Appender(Target implementationTarget) {
1✔
743
                    fieldDescription = implementationTarget.getInstrumentedType()
1✔
744
                            .getDeclaredFields()
1✔
745
                            .filter((named(AccessorProxy.FIELD_NAME)))
1✔
746
                            .getOnly();
1✔
747
                }
1✔
748

749
                /**
750
                 * {@inheritDoc}
751
                 */
752
                public Size apply(MethodVisitor methodVisitor,
753
                                  Context implementationContext,
754
                                  MethodDescription instrumentedMethod) {
755
                    StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
756
                            MethodVariableAccess.loadThis(),
1✔
757
                            MethodInvocation.invoke(StaticFieldConstructor.INSTANCE.objectTypeDefaultConstructor),
1✔
758
                            MethodVariableAccess.allArgumentsOf(instrumentedMethod.asDefined()).prependThisReference(),
1✔
759
                            FieldAccess.forField(fieldDescription).write(),
1✔
760
                            MethodReturn.VOID
761
                    ).apply(methodVisitor, implementationContext);
1✔
762
                    return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
763
                }
764
            }
765
        }
766

767
        /**
768
         * Implementation for a getter method.
769
         */
770
        @HashCodeAndEqualsPlugin.Enhance
771
        protected static class FieldGetter implements Implementation {
772

773
            /**
774
             * The field that is being accessed.
775
             */
776
            private final FieldDescription fieldDescription;
777

778
            /**
779
             * The assigner to use.
780
             */
781
            private final Assigner assigner;
782

783
            /**
784
             * The accessed type's method accessor factory.
785
             */
786
            private final MethodAccessorFactory methodAccessorFactory;
787

788
            /**
789
             * Creates a new getter implementation.
790
             *
791
             * @param fieldDescription      The field that is being accessed.
792
             * @param assigner              The assigner to use.
793
             * @param methodAccessorFactory The accessed type's method accessor factory.
794
             */
795
            protected FieldGetter(FieldDescription fieldDescription,
796
                                  Assigner assigner,
797
                                  MethodAccessorFactory methodAccessorFactory) {
1✔
798
                this.fieldDescription = fieldDescription;
1✔
799
                this.assigner = assigner;
1✔
800
                this.methodAccessorFactory = methodAccessorFactory;
1✔
801
            }
1✔
802

803
            /**
804
             * {@inheritDoc}
805
             */
806
            public InstrumentedType prepare(InstrumentedType instrumentedType) {
807
                return instrumentedType;
1✔
808
            }
809

810
            /**
811
             * {@inheritDoc}
812
             */
813
            public ByteCodeAppender appender(Target implementationTarget) {
814
                return new Appender(implementationTarget);
1✔
815
            }
816

817
            /**
818
             * A byte code appender for a getter method.
819
             */
820
            @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
821
            protected class Appender implements ByteCodeAppender {
822

823
                /**
824
                 * The generated accessor type.
825
                 */
826
                private final TypeDescription typeDescription;
827

828
                /**
829
                 * Creates a new appender for a setter method.
830
                 *
831
                 * @param implementationTarget The implementation target of the current instrumentation.
832
                 */
833
                protected Appender(Target implementationTarget) {
1✔
834
                    typeDescription = implementationTarget.getInstrumentedType();
1✔
835
                }
1✔
836

837
                /**
838
                 * {@inheritDoc}
839
                 */
840
                public Size apply(MethodVisitor methodVisitor,
841
                                  Context implementationContext,
842
                                  MethodDescription instrumentedMethod) {
843
                    MethodDescription getterMethod = methodAccessorFactory.registerGetterFor(fieldDescription, MethodAccessorFactory.AccessType.DEFAULT);
1✔
844
                    StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
845
                            fieldDescription.isStatic()
1✔
846
                                    ? StackManipulation.Trivial.INSTANCE
847
                                    : new StackManipulation.Compound(
848
                                    MethodVariableAccess.loadThis(),
1✔
849
                                    FieldAccess.forField(typeDescription.getDeclaredFields().filter((named(AccessorProxy.FIELD_NAME))).getOnly()).read()),
1✔
850
                            MethodInvocation.invoke(getterMethod),
1✔
851
                            assigner.assign(getterMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC),
1✔
852
                            MethodReturn.of(instrumentedMethod.getReturnType().asErasure())
1✔
853
                    ).apply(methodVisitor, implementationContext);
1✔
854
                    return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
855
                }
856
            }
857
        }
858

859
        /**
860
         * Implementation for a setter method.
861
         */
862
        @HashCodeAndEqualsPlugin.Enhance
863
        protected static class FieldSetter implements Implementation {
864

865
            /**
866
             * The field that is being accessed.
867
             */
868
            private final FieldDescription fieldDescription;
869

870
            /**
871
             * The assigner to use.
872
             */
873
            private final Assigner assigner;
874

875
            /**
876
             * The accessed type's method accessor factory.
877
             */
878
            private final MethodAccessorFactory methodAccessorFactory;
879

880
            /**
881
             * Creates a new setter implementation.
882
             *
883
             * @param fieldDescription      The field that is being accessed.
884
             * @param assigner              The assigner to use.
885
             * @param methodAccessorFactory The accessed type's method accessor factory.
886
             */
887
            protected FieldSetter(FieldDescription fieldDescription,
888
                                  Assigner assigner,
889
                                  MethodAccessorFactory methodAccessorFactory) {
1✔
890
                this.fieldDescription = fieldDescription;
1✔
891
                this.assigner = assigner;
1✔
892
                this.methodAccessorFactory = methodAccessorFactory;
1✔
893
            }
1✔
894

895
            /**
896
             * {@inheritDoc}
897
             */
898
            public InstrumentedType prepare(InstrumentedType instrumentedType) {
899
                return instrumentedType;
1✔
900
            }
901

902
            /**
903
             * {@inheritDoc}
904
             */
905
            public ByteCodeAppender appender(Target implementationTarget) {
906
                return new Appender(implementationTarget);
1✔
907
            }
908

909
            /**
910
             * A byte code appender for a setter method.
911
             */
912
            @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
913
            protected class Appender implements ByteCodeAppender {
914

915
                /**
916
                 * The generated accessor type.
917
                 */
918
                private final TypeDescription typeDescription;
919

920
                /**
921
                 * Creates a new appender for a setter method.
922
                 *
923
                 * @param implementationTarget The implementation target of the current instrumentation.
924
                 */
925
                protected Appender(Target implementationTarget) {
1✔
926
                    typeDescription = implementationTarget.getInstrumentedType();
1✔
927
                }
1✔
928

929
                /**
930
                 * {@inheritDoc}
931
                 */
932
                public Size apply(MethodVisitor methodVisitor,
933
                                  Context implementationContext,
934
                                  MethodDescription instrumentedMethod) {
935
                    TypeDescription.Generic parameterType = instrumentedMethod.getParameters().get(0).getType();
1✔
936
                    MethodDescription setterMethod = methodAccessorFactory.registerSetterFor(fieldDescription, MethodAccessorFactory.AccessType.DEFAULT);
1✔
937
                    StackManipulation.Size stackSize = new StackManipulation.Compound(
1✔
938
                            fieldDescription.isStatic()
1✔
939
                                    ? StackManipulation.Trivial.INSTANCE
940
                                    : new StackManipulation.Compound(
941
                                    MethodVariableAccess.loadThis(),
1✔
942
                                    FieldAccess.forField(typeDescription.getDeclaredFields()
1✔
943
                                            .filter((named(AccessorProxy.FIELD_NAME))).getOnly()).read()),
1✔
944
                            MethodVariableAccess.of(parameterType).loadFrom(1),
1✔
945
                            assigner.assign(parameterType, setterMethod.getParameters().get(0).getType(), Assigner.Typing.DYNAMIC),
1✔
946
                            MethodInvocation.invoke(setterMethod),
1✔
947
                            MethodReturn.VOID
948
                    ).apply(methodVisitor, implementationContext);
1✔
949
                    return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
1✔
950
                }
951
            }
952
        }
953

954
        /**
955
         * A proxy type for accessing a field either by a getter or a setter.
956
         */
957
        @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
958
        protected static class AccessorProxy extends StackManipulation.AbstractBase implements AuxiliaryType {
959

960
            /**
961
             * The name of the field that stores the accessed instance if any.
962
             */
963
            protected static final String FIELD_NAME = "instance";
964

965
            /**
966
             * The field that is being accessed.
967
             */
968
            private final FieldDescription fieldDescription;
969

970
            /**
971
             * The type which is accessed.
972
             */
973
            private final TypeDescription instrumentedType;
974

975
            /**
976
             * The field resolver to use.
977
             */
978
            private final FieldResolver fieldResolver;
979

980
            /**
981
             * The assigner to use.
982
             */
983
            private final Assigner assigner;
984

985
            /**
986
             * {@code true} if the generated proxy should be serializable.
987
             */
988
            private final boolean serializableProxy;
989

990
            /**
991
             * @param fieldDescription  The field that is being accessed.
992
             * @param instrumentedType  The type which is accessed.
993
             * @param fieldResolver     The field resolver to use.
994
             * @param assigner          The assigner to use.
995
             * @param serializableProxy {@code true} if the generated proxy should be serializable.
996
             */
997
            protected AccessorProxy(FieldDescription fieldDescription,
998
                                    TypeDescription instrumentedType,
999
                                    FieldResolver fieldResolver,
1000
                                    Assigner assigner,
1001
                                    boolean serializableProxy) {
1✔
1002
                this.fieldDescription = fieldDescription;
1✔
1003
                this.instrumentedType = instrumentedType;
1✔
1004
                this.fieldResolver = fieldResolver;
1✔
1005
                this.assigner = assigner;
1✔
1006
                this.serializableProxy = serializableProxy;
1✔
1007
            }
1✔
1008

1009
            /**
1010
             * {@inheritDoc}
1011
             */
1012
            public String getSuffix() {
1013
                return RandomString.hashOf(fieldDescription.hashCode()) + (serializableProxy ? "S" : "0");
1✔
1014
            }
1015

1016
            /**
1017
             * {@inheritDoc}
1018
             */
1019
            public DynamicType make(String auxiliaryTypeName,
1020
                                    ClassFileVersion classFileVersion,
1021
                                    MethodAccessorFactory methodAccessorFactory) {
1022
                return fieldResolver.apply(new ByteBuddy(classFileVersion)
1✔
1023
                        .with(TypeValidation.DISABLED)
1✔
1024
                        .subclass(fieldResolver.getProxyType(), ConstructorStrategy.Default.NO_CONSTRUCTORS)
1✔
1025
                        .name(auxiliaryTypeName)
1✔
1026
                        .modifiers(DEFAULT_TYPE_MODIFIER)
1✔
1027
                        .implement(serializableProxy ? new Class<?>[]{Serializable.class} : new Class<?>[0])
1✔
1028
                        .defineConstructor().withParameters(fieldDescription.isStatic()
1✔
1029
                                ? Collections.<TypeDescription>emptyList()
1✔
1030
                                : Collections.singletonList(instrumentedType))
1✔
1031
                        .intercept(fieldDescription.isStatic()
1✔
1032
                                ? StaticFieldConstructor.INSTANCE
1033
                                : new InstanceFieldConstructor(instrumentedType)), fieldDescription, assigner, methodAccessorFactory).make();
1✔
1034
            }
1035

1036
            /**
1037
             * {@inheritDoc}
1038
             */
1039
            public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
1040
                TypeDescription auxiliaryType = implementationContext.register(this);
1✔
1041
                return new Compound(
1✔
1042
                        TypeCreation.of(auxiliaryType),
1✔
1043
                        Duplication.SINGLE,
1044
                        fieldDescription.isStatic()
1✔
1045
                                ? Trivial.INSTANCE
1046
                                : MethodVariableAccess.loadThis(),
1✔
1047
                        MethodInvocation.invoke(auxiliaryType.getDeclaredMethods().filter(isConstructor()).getOnly())
1✔
1048
                ).apply(methodVisitor, implementationContext);
1✔
1049
            }
1050
        }
1051
    }
1052
}
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