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

raphw / byte-buddy / #717

17 Jan 2025 09:06PM UTC coverage: 85.373% (-0.1%) from 85.479%
#717

push

raphw
Fix build.

0 of 4 new or added lines in 1 file covered. (0.0%)

572 existing lines in 8 files now uncovered.

28973 of 33937 relevant lines covered (85.37%)

0.85 hits per line

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

72.42
/byte-buddy-dep/src/main/java/net/bytebuddy/asm/MemberSubstitution.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.asm;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ClassFileVersion;
20
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
21
import net.bytebuddy.description.ByteCodeElement;
22
import net.bytebuddy.description.annotation.AnnotationDescription;
23
import net.bytebuddy.description.annotation.AnnotationValue;
24
import net.bytebuddy.description.enumeration.EnumerationDescription;
25
import net.bytebuddy.description.field.FieldDescription;
26
import net.bytebuddy.description.field.FieldList;
27
import net.bytebuddy.description.method.MethodDescription;
28
import net.bytebuddy.description.method.MethodList;
29
import net.bytebuddy.description.method.ParameterDescription;
30
import net.bytebuddy.description.type.TypeDefinition;
31
import net.bytebuddy.description.type.TypeDescription;
32
import net.bytebuddy.description.type.TypeList;
33
import net.bytebuddy.description.type.TypeVariableToken;
34
import net.bytebuddy.dynamic.ClassFileLocator;
35
import net.bytebuddy.dynamic.TargetType;
36
import net.bytebuddy.dynamic.scaffold.FieldLocator;
37
import net.bytebuddy.dynamic.scaffold.MethodGraph;
38
import net.bytebuddy.implementation.Implementation;
39
import net.bytebuddy.implementation.bytecode.*;
40
import net.bytebuddy.implementation.bytecode.assign.Assigner;
41
import net.bytebuddy.implementation.bytecode.collection.ArrayFactory;
42
import net.bytebuddy.implementation.bytecode.constant.*;
43
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
44
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
45
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
46
import net.bytebuddy.matcher.ElementMatcher;
47
import net.bytebuddy.matcher.ElementMatchers;
48
import net.bytebuddy.pool.TypePool;
49
import net.bytebuddy.utility.*;
50
import net.bytebuddy.utility.nullability.MaybeNull;
51
import net.bytebuddy.utility.visitor.LocalVariableAwareMethodVisitor;
52
import org.objectweb.asm.MethodVisitor;
53
import org.objectweb.asm.Opcodes;
54

55
import java.io.Serializable;
56
import java.lang.annotation.*;
57
import java.lang.reflect.Constructor;
58
import java.lang.reflect.Field;
59
import java.lang.reflect.Method;
60
import java.lang.reflect.Type;
61
import java.util.*;
62

63
import static net.bytebuddy.matcher.ElementMatchers.*;
64

65
/**
66
 * <p>
67
 * Substitutes field access, method invocations or constructor calls within a method's body.
68
 * </p>
69
 * <p>
70
 * <b>Note</b>: This substitution must not be used to match constructor calls to an instrumented class's super constructor invocation from
71
 * within a constructor. Matching such constructors will result in an invalid stack and a verification error.
72
 * </p>
73
 * <p>
74
 * <b>Note</b>: This visitor will compute the required stack size on a best effort basis. For allocating an optimal stack size, ASM needs
75
 * to be configured to compute the stack size.
76
 * </p>
77
 * <p>
78
 * <b>Important</b>: This component relies on using a {@link TypePool} for locating types within method bodies. Within a redefinition
79
 * or a rebasement, this type pool normally resolved correctly by Byte Buddy. When subclassing a type, the type pool must be set
80
 * explicitly, using {@link net.bytebuddy.dynamic.DynamicType.Builder#make(TypePool)} or any similar method. It is however not normally
81
 * necessary to use this component when subclassing a type where methods are only defined explicitly.
82
 * </p>
83
 */
84
@HashCodeAndEqualsPlugin.Enhance
85
public class MemberSubstitution implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
86

87
    /**
88
     * The index of the this reference within a non-static method.
89
     */
90
    protected static final int THIS_REFERENCE = 0;
91

92
    /**
93
     * The method graph compiler to use.
94
     */
95
    private final MethodGraph.Compiler methodGraphCompiler;
96

97
    /**
98
     * The type pool resolver to use.
99
     */
100
    private final TypePoolResolver typePoolResolver;
101

102
    /**
103
     * {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
104
     */
105
    private final boolean strict;
106

107
    /**
108
     * {@code true} if the instrumentation should fail if applied to a method without match.
109
     */
110
    private final boolean failIfNoMatch;
111

112
    /**
113
     * The replacement factory to use.
114
     */
115
    private final Replacement.Factory replacementFactory;
116

117
    /**
118
     * Creates a default member substitution.
119
     *
120
     * @param strict {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
121
     */
122
    protected MemberSubstitution(boolean strict) {
123
        this(MethodGraph.Compiler.DEFAULT, TypePoolResolver.OfImplicitPool.INSTANCE, strict, false, Replacement.NoOp.INSTANCE);
1✔
124
    }
1✔
125

126
    /**
127
     * Creates a new member substitution.
128
     *
129
     * @param methodGraphCompiler The method graph compiler to use.
130
     * @param typePoolResolver    The type pool resolver to use.
131
     * @param strict              {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
132
     * @param failIfNoMatch       {@code true} if the instrumentation should fail if applied to a method without match.
133
     * @param replacementFactory  The replacement factory to use.
134
     */
135
    protected MemberSubstitution(MethodGraph.Compiler methodGraphCompiler,
136
                                 TypePoolResolver typePoolResolver,
137
                                 boolean strict,
138
                                 boolean failIfNoMatch,
139
                                 Replacement.Factory replacementFactory) {
1✔
140
        this.methodGraphCompiler = methodGraphCompiler;
1✔
141
        this.typePoolResolver = typePoolResolver;
1✔
142
        this.failIfNoMatch = failIfNoMatch;
1✔
143
        this.strict = strict;
1✔
144
        this.replacementFactory = replacementFactory;
1✔
145
    }
1✔
146

147
    /**
148
     * Creates a member substitution that requires the resolution of all fields and methods that are referenced within a method body. Doing so,
149
     * this component raises an exception if any member cannot be resolved what makes this component unusable when facing optional types.
150
     *
151
     * @return A strict member substitution.
152
     */
153
    public static MemberSubstitution strict() {
154
        return new MemberSubstitution(true);
1✔
155
    }
156

157
    /**
158
     * Creates a member substitution that skips any unresolvable fields or methods that are referenced within a method body. Using a relaxed
159
     * member substitution, methods containing optional types are supported. In the process, it is however possible that misconfigurations
160
     * of this component remain undiscovered.
161
     *
162
     * @return A relaxed member substitution.
163
     */
164
    public static MemberSubstitution relaxed() {
165
        return new MemberSubstitution(false);
1✔
166
    }
167

168
    /**
169
     * Substitutes any interaction with a field or method that matches the given matcher.
170
     *
171
     * @param matcher The matcher to determine what access to byte code elements to substitute.
172
     * @return A specification that allows to determine how to substitute any interaction with byte code elements that match the supplied matcher.
173
     */
174
    public WithoutSpecification element(ElementMatcher<? super ByteCodeElement.Member> matcher) {
175
        return new WithoutSpecification.ForMatchedByteCodeElement(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher);
×
176
    }
177

178
    /**
179
     * Substitutes any field access that matches the given matcher.
180
     *
181
     * @param matcher The matcher to determine what fields to substitute.
182
     * @return A specification that allows to determine how to substitute any field access that match the supplied matcher.
183
     */
184
    public WithoutSpecification.ForMatchedField field(ElementMatcher<? super FieldDescription> matcher) {
185
        return new WithoutSpecification.ForMatchedField(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher);
1✔
186
    }
187

188
    /**
189
     * Substitutes any method invocation that matches the given matcher.
190
     *
191
     * @param matcher The matcher to determine what methods to substitute.
192
     * @return A specification that allows to determine how to substitute any method invocations that match the supplied matcher.
193
     */
194
    public WithoutSpecification.ForMatchedMethod method(ElementMatcher<? super MethodDescription> matcher) {
195
        return new WithoutSpecification.ForMatchedMethod(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher);
1✔
196
    }
197

198
    /**
199
     * Substitutes any constructor invocation that matches the given matcher.
200
     *
201
     * @param matcher The matcher to determine what constructors to substitute.
202
     * @return A specification that allows to determine how to substitute any constructor invocations that match the supplied matcher.
203
     */
204
    public WithoutSpecification constructor(ElementMatcher<? super MethodDescription> matcher) {
205
        return invokable(isConstructor().and(matcher));
1✔
206
    }
207

208
    /**
209
     * Substitutes any method or constructor invocation that matches the given matcher.
210
     *
211
     * @param matcher The matcher to determine what method or constructors to substitute.
212
     * @return A specification that allows to determine how to substitute any constructor invocations that match the supplied matcher.
213
     */
214
    public WithoutSpecification invokable(ElementMatcher<? super MethodDescription> matcher) {
215
        return new WithoutSpecification.ForMatchedMethod(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher);
1✔
216
    }
217

218
    /**
219
     * Specifies the use of a specific method graph compiler for the resolution of virtual methods.
220
     *
221
     * @param methodGraphCompiler The method graph compiler to use.
222
     * @return A new member substitution that is equal to this but uses the specified method graph compiler.
223
     */
224
    public MemberSubstitution with(MethodGraph.Compiler methodGraphCompiler) {
225
        return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory);
×
226
    }
227

228
    /**
229
     * Specifies a type pool resolver to be used for locating members.
230
     *
231
     * @param typePoolResolver The type pool resolver to use.
232
     * @return A new instance of this member substitution that uses the supplied type pool resolver.
233
     */
234
    public MemberSubstitution with(TypePoolResolver typePoolResolver) {
235
        return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory);
×
236
    }
237

238
    /**
239
     * Specifies if this substitution should fail if applied on a method without a match.
240
     *
241
     * @param failIfNoMatch {@code true} if the instrumentation should fail if applied to a method without match.
242
     * @return A new instance of this member substitution that fails if applied on a method without a match.
243
     */
244
    public MemberSubstitution failIfNoMatch(boolean failIfNoMatch) {
245
        return new MemberSubstitution(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory);
1✔
246
    }
247

248
    /**
249
     * Applies this member substitution to any method that matches the supplied matcher.
250
     *
251
     * @param matcher The matcher to determine this substitutions application.
252
     * @return An ASM visitor wrapper that applies all specified substitutions for any matched method.
253
     */
254
    public AsmVisitorWrapper.ForDeclaredMethods on(ElementMatcher<? super MethodDescription> matcher) {
255
        return new AsmVisitorWrapper.ForDeclaredMethods().invokable(matcher, this);
1✔
256
    }
257

258
    /**
259
     * {@inheritDoc}
260
     */
261
    public MethodVisitor wrap(TypeDescription instrumentedType,
262
                              MethodDescription instrumentedMethod,
263
                              MethodVisitor methodVisitor,
264
                              Implementation.Context implementationContext,
265
                              TypePool typePool,
266
                              int writerFlags,
267
                              int readerFlags) {
268
        typePool = typePoolResolver.resolve(instrumentedType, instrumentedMethod, typePool);
1✔
269
        return new SubstitutingMethodVisitor(methodVisitor,
1✔
270
                instrumentedType,
271
                instrumentedMethod,
272
                methodGraphCompiler,
273
                strict,
274
                failIfNoMatch,
275
                replacementFactory.make(instrumentedType, instrumentedMethod, typePool),
1✔
276
                implementationContext,
277
                typePool,
278
                implementationContext.getClassFileVersion().isAtLeast(ClassFileVersion.JAVA_V11));
1✔
279
    }
280

281
    /**
282
     * A member substitution that lacks a specification for how to substitute the matched members references within a method body.
283
     */
284
    @HashCodeAndEqualsPlugin.Enhance
285
    public abstract static class WithoutSpecification {
286

287
        /**
288
         * The method graph compiler to use.
289
         */
290
        protected final MethodGraph.Compiler methodGraphCompiler;
291

292
        /**
293
         * The type pool resolver to use.
294
         */
295
        protected final TypePoolResolver typePoolResolver;
296

297
        /**
298
         * {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
299
         */
300
        protected final boolean strict;
301

302
        /**
303
         * {@code true} if the instrumentation should fail if applied to a method without match.
304
         */
305
        protected final boolean failIfNoMatch;
306

307
        /**
308
         * The replacement factory to use for creating substitutions.
309
         */
310
        protected final Replacement.Factory replacementFactory;
311

312
        /**
313
         * Creates a new member substitution that requires a specification for how to perform a substitution.
314
         *
315
         * @param methodGraphCompiler The method graph compiler to use.
316
         * @param typePoolResolver    The type pool resolver to use.
317
         * @param strict              {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
318
         * @param failIfNoMatch       {@code true} if the instrumentation should fail if applied to a method without match.
319
         * @param replacementFactory  The replacement factory to use for creating substitutions.
320
         */
321
        protected WithoutSpecification(MethodGraph.Compiler methodGraphCompiler,
322
                                       TypePoolResolver typePoolResolver,
323
                                       boolean strict,
324
                                       boolean failIfNoMatch,
325
                                       Replacement.Factory replacementFactory) {
1✔
326
            this.methodGraphCompiler = methodGraphCompiler;
1✔
327
            this.typePoolResolver = typePoolResolver;
1✔
328
            this.strict = strict;
1✔
329
            this.failIfNoMatch = failIfNoMatch;
1✔
330
            this.replacementFactory = replacementFactory;
1✔
331
        }
1✔
332

333
        /**
334
         * Subs any interaction with a matched byte code element. Any value read from the element will be replaced with the stubbed
335
         * value's default, i.e. {@code null} for reference types and the specific {@code 0} value for primitive types. Any written
336
         * value will simply be discarded.
337
         *
338
         * @return A member substitution that stubs any interaction with a matched byte code element.
339
         */
340
        public MemberSubstitution stub() {
341
            return replaceWith(Substitution.Stubbing.INSTANCE);
1✔
342
        }
343

344
        /**
345
         * Replaces any interaction with a matched byte code element with the provided compile-time constant.
346
         *
347
         * @param value The compile-time constant to set.
348
         * @return A member substitution that replaces any interaction with the supplied compile-time constant.
349
         */
350
        public MemberSubstitution replaceWithConstant(Object value) {
351
            ConstantValue constant = ConstantValue.Simple.wrap(value);
1✔
352
            return replaceWith(new Substitution.ForValue(constant.toStackManipulation(), constant.getTypeDescription().asGenericType()));
1✔
353
        }
354

355
        /**
356
         * <p>
357
         * Replaces any interaction with a matched byte code element by an interaction with the specified field. If a field
358
         * is replacing a method or constructor invocation, it is treated as if it was a field getter or setter respectively.
359
         * </p>
360
         * <p>
361
         * A replacement can only be applied if the field is compatible to the original byte code element, i.e. consumes an
362
         * instance of the declaring type if it is not {@code static} as an argument and consumes or produces an instance of
363
         * the field's type.
364
         * </p>
365
         *
366
         * @param field The field to access instead of interacting with any of the matched byte code elements.
367
         * @return A member substitution that replaces any matched byte code element with an access of the specified field.
368
         */
369
        public MemberSubstitution replaceWith(Field field) {
370
            return replaceWith(new FieldDescription.ForLoadedField(field));
1✔
371
        }
372

373
        /**
374
         * <p>
375
         * Replaces any interaction with a matched byte code element by an interaction with the specified field. If a field
376
         * is replacing a method or constructor invocation, it is treated as if it was a field getter or setter respectively.
377
         * </p>
378
         * <p>
379
         * A replacement can only be applied if the field is compatible to the original byte code element, i.e. consumes an
380
         * instance of the declaring type if it is not {@code static} as an argument and consumes or produces an instance of
381
         * the field's type.
382
         * </p>
383
         *
384
         * @param fieldDescription The field to access instead of interacting with any of the matched byte code elements.
385
         * @return A member substitution that replaces any matched byte code element with an access of the specified field.
386
         */
387
        public MemberSubstitution replaceWith(FieldDescription fieldDescription) {
388
            return replaceWith(new Substitution.ForFieldAccess.OfGivenField(fieldDescription));
1✔
389
        }
390

391
        /**
392
         * Replaces any interaction with a matched byte code element with a non-static field access on the first
393
         * parameter of the matched element. When matching a non-static field access or method invocation, the
394
         * substituted field is located on the same receiver type as the original access. For static access, the
395
         * first argument is used as a receiver.
396
         *
397
         * @param matcher A matcher for locating a field on the original interaction's receiver type.
398
         * @return A member substitution that replaces any matched byte code element with an access of the matched field.
399
         */
400
        public MemberSubstitution replaceWithField(ElementMatcher<? super FieldDescription> matcher) {
401
            return replaceWith(new Substitution.ForFieldAccess.OfMatchedField(matcher));
1✔
402
        }
403

404
        /**
405
         * <p>
406
         * Replaces any interaction with a matched byte code element by an invocation of the specified method. If a method
407
         * is replacing a field access, it is treated as if it was replacing an invocation of the field's getter or setter respectively.
408
         * </p>
409
         * <p>
410
         * A replacement can only be applied if the method is compatible to the original byte code element, i.e. consumes compatible
411
         * arguments and returns a compatible value. If the method is not {@code static}, it is treated as if {@code this} was an implicit
412
         * first argument.
413
         * </p>
414
         *
415
         * @param method The method to invoke instead of interacting with any of the matched byte code elements.
416
         * @return A member substitution that replaces any matched byte code element with an invocation of the specified method.
417
         */
418
        public MemberSubstitution replaceWith(Method method) {
419
            return replaceWith(new MethodDescription.ForLoadedMethod(method));
1✔
420
        }
421

422
        /**
423
         * <p>
424
         * Replaces any interaction with a matched byte code element by an invocation of the specified method. If a method
425
         * is replacing a field access, it is treated as if it was replacing an invocation of the field's getter or setter respectively.
426
         * </p>
427
         * <p>
428
         * A replacement can only be applied if the method is compatible to the original byte code element, i.e. consumes compatible
429
         * arguments and returns a compatible value. If the method is not {@code static}, it is treated as if {@code this} was an implicit
430
         * first argument.
431
         * </p>
432
         * <p>
433
         * <b>Important</b>: It is not allowed to specify a constructor or the static type initializer as a replacement.
434
         * </p>
435
         *
436
         * @param methodDescription The method to invoke instead of interacting with any of the matched byte code elements.
437
         * @return A member substitution that replaces any matched byte code element with an invocation of the specified method.
438
         */
439
        public MemberSubstitution replaceWith(MethodDescription methodDescription) {
440
            if (!methodDescription.isMethod()) {
1✔
441
                throw new IllegalArgumentException("Cannot use " + methodDescription + " as a replacement");
1✔
442
            }
443
            return replaceWith(new Substitution.ForMethodInvocation.OfGivenMethod(methodDescription));
1✔
444
        }
445

446
        /**
447
         * Replaces any interaction with a matched byte code element with a non-static method access on the first
448
         * parameter of the matched element. When matching a non-static field access or method invocation, the
449
         * substituted method is located on the same receiver type as the original access. For static access, the
450
         * first argument is used as a receiver.
451
         *
452
         * @param matcher A matcher for locating a method on the original interaction's receiver type.
453
         * @return A member substitution that replaces any matched byte code element with an access of the matched method.
454
         */
455
        public MemberSubstitution replaceWithMethod(ElementMatcher<? super MethodDescription> matcher) {
456
            return replaceWithMethod(matcher, methodGraphCompiler);
1✔
457
        }
458

459
        /**
460
         * Replaces any interaction with a matched byte code element with a non-static method access on the first
461
         * parameter of the matched element. When matching a non-static field access or method invocation, the
462
         * substituted method is located on the same receiver type as the original access. For static access, the
463
         * first argument is used as a receiver.
464
         *
465
         * @param matcher             A matcher for locating a method on the original interaction's receiver type.
466
         * @param methodGraphCompiler The method graph compiler to use for locating a method.
467
         * @return A member substitution that replaces any matched byte code element with an access of the matched method.
468
         */
469
        public MemberSubstitution replaceWithMethod(ElementMatcher<? super MethodDescription> matcher, MethodGraph.Compiler methodGraphCompiler) {
470
            return replaceWith(new Substitution.ForMethodInvocation.OfMatchedMethod(matcher, methodGraphCompiler));
1✔
471
        }
472

473
        /**
474
         * Replaces any interaction with a matched byte code element with an invocation of the instrumented
475
         * method. This can cause an infinite recursive call if the arguments to the method are not altered.
476
         *
477
         * @return A member substitution that replaces any matched byte code element with an invocation of the
478
         * instrumented method.
479
         */
480
        public MemberSubstitution replaceWithInstrumentedMethod() {
481
            return replaceWith(Substitution.ForMethodInvocation.OfInstrumentedMethod.INSTANCE);
1✔
482
        }
483

484
        /**
485
         * Replaces the matched byte code elements with a chain of substitutions that can operate on the same values as the substituted element. This is a
486
         * shortcut for creating a substitution chain with a default assigner.
487
         *
488
         * @param step The steps to apply for a substitution.
489
         * @return A member substitution that replaces any matched byte code element with the provided substitution chain.
490
         */
491
        public MemberSubstitution replaceWithChain(Substitution.Chain.Step.Factory... step) {
492
            return replaceWithChain(Arrays.asList(step));
1✔
493
        }
494

495
        /**
496
         * Replaces the matched byte code elements with a chain of substitutions that can operate on the same values as the substituted element. This is a
497
         * shortcut for creating a substitution chain with a default assigner.
498
         *
499
         * @param steps The steps to apply for a substitution.
500
         * @return A member substitution that replaces any matched byte code element with the provided substitution chain.
501
         */
502
        public MemberSubstitution replaceWithChain(List<? extends Substitution.Chain.Step.Factory> steps) {
503
            return replaceWith(Substitution.Chain.withDefaultAssigner().executing(steps));
1✔
504
        }
505

506
        /**
507
         * Replaces any interaction with the supplied substitution.
508
         *
509
         * @param factory The substitution factory to use for creating the applied substitution.
510
         * @return A member substitution that replaces any matched byte code element with the supplied substitution.
511
         */
512
        public abstract MemberSubstitution replaceWith(Substitution.Factory factory);
513

514
        /**
515
         * Describes a member substitution that requires a specification for how to replace a byte code element.
516
         */
517
        @HashCodeAndEqualsPlugin.Enhance
518
        protected static class ForMatchedByteCodeElement extends WithoutSpecification {
519

520
            /**
521
             * A matcher for any byte code elements that should be substituted.
522
             */
523
            private final ElementMatcher<? super ByteCodeElement.Member> matcher;
524

525
            /**
526
             * Creates a new member substitution for a matched byte code element that requires a specification for how to perform a substitution.
527
             *
528
             * @param methodGraphCompiler The method graph compiler to use.
529
             * @param typePoolResolver    The type pool resolver to use.
530
             * @param strict              {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
531
             * @param failIfNoMatch       {@code true} if the instrumentation should fail if applied to a method without match.
532
             * @param replacementFactory  The replacement factory to use.
533
             * @param matcher             A matcher for any byte code elements that should be substituted.
534
             */
535
            protected ForMatchedByteCodeElement(MethodGraph.Compiler methodGraphCompiler,
536
                                                TypePoolResolver typePoolResolver,
537
                                                boolean strict,
538
                                                boolean failIfNoMatch,
539
                                                Replacement.Factory replacementFactory,
540
                                                ElementMatcher<? super ByteCodeElement.Member> matcher) {
541
                super(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory);
×
542
                this.matcher = matcher;
×
543
            }
×
544

545
            /**
546
             * {@inheritDoc}
547
             */
548
            public MemberSubstitution replaceWith(Substitution.Factory substitutionFactory) {
549
                return new MemberSubstitution(methodGraphCompiler,
×
550
                        typePoolResolver,
551
                        strict,
552
                        failIfNoMatch,
553
                        new Replacement.Factory.Compound(this.replacementFactory, Replacement.ForElementMatchers.Factory.of(matcher, substitutionFactory)));
×
554
            }
555
        }
556

557
        /**
558
         * Describes a member substitution that requires a specification for how to replace a field.
559
         */
560
        @HashCodeAndEqualsPlugin.Enhance
561
        public static class ForMatchedField extends WithoutSpecification {
562

563
            /**
564
             * A matcher for any field that should be substituted.
565
             */
566
            private final ElementMatcher<? super FieldDescription> matcher;
567

568
            /**
569
             * {@code true} if read access to a field should be substituted.
570
             */
571
            private final boolean matchRead;
572

573
            /**
574
             * {@code true} if write access to a field should be substituted.
575
             */
576
            private final boolean matchWrite;
577

578
            /**
579
             * Creates a new member substitution for a matched field that requires a specification for how to perform a substitution.
580
             *
581
             * @param methodGraphCompiler The method graph compiler to use.
582
             * @param typePoolResolver    The type pool resolver to use.
583
             * @param strict              {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
584
             * @param failIfNoMatch       {@code true} if the instrumentation should fail if applied to a method without match.
585
             * @param replacementFactory  The replacement factory to use.
586
             * @param matcher             A matcher for any field that should be substituted.
587
             */
588
            protected ForMatchedField(MethodGraph.Compiler methodGraphCompiler,
589
                                      TypePoolResolver typePoolResolver,
590
                                      boolean strict,
591
                                      boolean failIfNoMatch,
592
                                      Replacement.Factory replacementFactory,
593
                                      ElementMatcher<? super FieldDescription> matcher) {
594
                this(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher, true, true);
1✔
595
            }
1✔
596

597
            /**
598
             * Creates a new member substitution for a matched field that requires a specification for how to perform a substitution.
599
             *
600
             * @param methodGraphCompiler The method graph compiler to use.
601
             * @param typePoolResolver    The type pool resolver to use.
602
             * @param strict              {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
603
             * @param failIfNoMatch       {@code true} if the instrumentation should fail if applied to a method without match.
604
             * @param replacementFactory  The replacement factory to use.
605
             * @param matcher             A matcher for any field that should be substituted.
606
             * @param matchRead           {@code true} if read access to a field should be substituted.
607
             * @param matchWrite          {@code true} if write access to a field should be substituted.
608
             */
609
            protected ForMatchedField(MethodGraph.Compiler methodGraphCompiler,
610
                                      TypePoolResolver typePoolResolver,
611
                                      boolean strict,
612
                                      boolean failIfNoMatch,
613
                                      Replacement.Factory replacementFactory,
614
                                      ElementMatcher<? super FieldDescription> matcher,
615
                                      boolean matchRead,
616
                                      boolean matchWrite) {
617
                super(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory);
1✔
618
                this.matcher = matcher;
1✔
619
                this.matchRead = matchRead;
1✔
620
                this.matchWrite = matchWrite;
1✔
621
            }
1✔
622

623
            /**
624
             * When invoked, only read access of the previously matched field is substituted.
625
             *
626
             * @return This instance with the limitation that only read access to the matched field is substituted.
627
             */
628
            public WithoutSpecification onRead() {
629
                return new ForMatchedField(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher, true, false);
1✔
630
            }
631

632
            /**
633
             * When invoked, only write access of the previously matched field is substituted.
634
             *
635
             * @return This instance with the limitation that only write access to the matched field is substituted.
636
             */
637
            public WithoutSpecification onWrite() {
638
                return new ForMatchedField(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher, false, true);
1✔
639
            }
640

641
            /**
642
             * {@inheritDoc}
643
             */
644
            public MemberSubstitution replaceWith(Substitution.Factory substitutionFactory) {
645
                return new MemberSubstitution(methodGraphCompiler,
1✔
646
                        typePoolResolver,
647
                        strict,
648
                        failIfNoMatch,
649
                        new Replacement.Factory.Compound(this.replacementFactory, Replacement.ForElementMatchers.Factory.ofField(matcher, matchRead, matchWrite, substitutionFactory)));
1✔
650
            }
651
        }
652

653
        /**
654
         * Describes a member substitution that requires a specification for how to replace a method or constructor.
655
         */
656
        @HashCodeAndEqualsPlugin.Enhance
657
        public static class ForMatchedMethod extends WithoutSpecification {
658

659
            /**
660
             * A matcher for any method or constructor that should be substituted.
661
             */
662
            private final ElementMatcher<? super MethodDescription> matcher;
663

664
            /**
665
             * {@code true} if this specification includes virtual invocations.
666
             */
667
            private final boolean includeVirtualCalls;
668

669
            /**
670
             * {@code true} if this specification includes {@code super} invocations.
671
             */
672
            private final boolean includeSuperCalls;
673

674
            /**
675
             * Creates a new member substitution for a matched method that requires a specification for how to perform a substitution.
676
             *
677
             * @param methodGraphCompiler The method graph compiler to use.
678
             * @param typePoolResolver    The type pool resolver to use.
679
             * @param strict              {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
680
             * @param failIfNoMatch       {@code true} if the instrumentation should fail if applied to a method without match.
681
             * @param replacementFactory  The replacement factory to use.
682
             * @param matcher             A matcher for any method or constructor that should be substituted.
683
             */
684
            protected ForMatchedMethod(MethodGraph.Compiler methodGraphCompiler,
685
                                       TypePoolResolver typePoolResolver,
686
                                       boolean strict,
687
                                       boolean failIfNoMatch,
688
                                       Replacement.Factory replacementFactory,
689
                                       ElementMatcher<? super MethodDescription> matcher) {
690
                this(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory, matcher, true, true);
1✔
691
            }
1✔
692

693
            /**
694
             * Creates a new member substitution for a matched method that requires a specification for how to perform a substitution.
695
             *
696
             * @param methodGraphCompiler The method graph compiler to use.
697
             * @param typePoolResolver    The type pool resolver to use.
698
             * @param strict              {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
699
             * @param failIfNoMatch       {@code true} if the instrumentation should fail if applied to a method without match.
700
             * @param replacementFactory  The replacement factory to use.
701
             * @param matcher             A matcher for any method or constructor that should be substituted.
702
             * @param includeVirtualCalls {@code true} if this specification includes virtual invocations.
703
             * @param includeSuperCalls   {@code true} if this specification includes {@code super} invocations.
704
             */
705
            protected ForMatchedMethod(MethodGraph.Compiler methodGraphCompiler,
706
                                       TypePoolResolver typePoolResolver,
707
                                       boolean strict,
708
                                       boolean failIfNoMatch,
709
                                       Replacement.Factory replacementFactory,
710
                                       ElementMatcher<? super MethodDescription> matcher,
711
                                       boolean includeVirtualCalls,
712
                                       boolean includeSuperCalls) {
713
                super(methodGraphCompiler, typePoolResolver, strict, failIfNoMatch, replacementFactory);
1✔
714
                this.matcher = matcher;
1✔
715
                this.includeVirtualCalls = includeVirtualCalls;
1✔
716
                this.includeSuperCalls = includeSuperCalls;
1✔
717
            }
1✔
718

719
            /**
720
             * Limits the substituted method calls to method calls that invoke a method virtually (as opposed to a {@code super} invocation).
721
             *
722
             * @return This specification where only virtual methods are matched if they are not invoked as a virtual call.
723
             */
724
            public WithoutSpecification onVirtualCall() {
725
                return new ForMatchedMethod(methodGraphCompiler,
1✔
726
                        typePoolResolver,
727
                        strict,
728
                        failIfNoMatch,
729
                        replacementFactory,
730
                        isVirtual().and(matcher),
1✔
731
                        true,
732
                        false);
733
            }
734

735
            /**
736
             * Limits the substituted method calls to method calls that invoke a method as a {@code super} call.
737
             *
738
             * @return This specification where only virtual methods are matched if they are not invoked as a super call.
739
             */
740
            public WithoutSpecification onSuperCall() {
741
                return new ForMatchedMethod(methodGraphCompiler,
1✔
742
                        typePoolResolver,
743
                        strict,
744
                        failIfNoMatch,
745
                        replacementFactory,
746
                        isVirtual().and(matcher),
1✔
747
                        false,
748
                        true);
749
            }
750

751
            /**
752
             * {@inheritDoc}
753
             */
754
            public MemberSubstitution replaceWith(Substitution.Factory substitutionFactory) {
755
                return new MemberSubstitution(methodGraphCompiler,
1✔
756
                        typePoolResolver,
757
                        strict,
758
                        failIfNoMatch,
759
                        new Replacement.Factory.Compound(this.replacementFactory, Replacement.ForElementMatchers.Factory.ofMethod(matcher, includeVirtualCalls, includeSuperCalls, substitutionFactory)));
1✔
760
            }
761
        }
762
    }
763

764
    /**
765
     * A type pool resolver is responsible for resolving a {@link TypePool} for locating substituted members.
766
     */
767
    public interface TypePoolResolver {
768

769
        /**
770
         * Resolves a type pool to use for locating substituted members.
771
         *
772
         * @param instrumentedType   The instrumented type.
773
         * @param instrumentedMethod The instrumented method.
774
         * @param typePool           The type pool implicit to the instrumentation.
775
         * @return The type pool to use.
776
         */
777
        TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool);
778

779
        /**
780
         * Returns the implicit type pool.
781
         */
782
        enum OfImplicitPool implements TypePoolResolver {
1✔
783

784
            /**
785
             * The singleton instance.
786
             */
787
            INSTANCE;
1✔
788

789
            /**
790
             * {@inheritDoc}
791
             */
792
            public TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
793
                return typePool;
1✔
794
            }
795
        }
796

797
        /**
798
         * A type pool resolver that returns a specific type pool.
799
         */
800
        @HashCodeAndEqualsPlugin.Enhance
801
        class ForExplicitPool implements TypePoolResolver {
802

803
            /**
804
             * The type pool to return.
805
             */
806
            private final TypePool typePool;
807

808
            /**
809
             * Creates a resolver for an explicit type pool.
810
             *
811
             * @param typePool The type pool to return.
812
             */
813
            public ForExplicitPool(TypePool typePool) {
×
814
                this.typePool = typePool;
×
815
            }
×
816

817
            /**
818
             * {@inheritDoc}
819
             */
820
            public TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
821
                return this.typePool;
×
822
            }
823
        }
824

825
        /**
826
         * A type pool resolver that resolves the implicit pool but additionally checks another class file locator.
827
         */
828
        @HashCodeAndEqualsPlugin.Enhance
829
        class ForClassFileLocator implements TypePoolResolver {
830

831
            /**
832
             * The class file locator to use.
833
             */
834
            private final ClassFileLocator classFileLocator;
835

836
            /**
837
             * The reader mode to apply.
838
             */
839
            private final TypePool.Default.ReaderMode readerMode;
840

841
            /**
842
             * Creates a new type pool resolver for a class file locator as a supplement of the implicit type pool.
843
             *
844
             * @param classFileLocator The class file locator to use.
845
             */
846
            public ForClassFileLocator(ClassFileLocator classFileLocator) {
847
                this(classFileLocator, TypePool.Default.ReaderMode.FAST);
×
848
            }
×
849

850
            /**
851
             * Creates a new type pool resolver for a class file locator as a supplement of the implicit type pool.
852
             *
853
             * @param classFileLocator The class file locator to use.
854
             * @param readerMode       The reader mode to apply.
855
             */
856
            public ForClassFileLocator(ClassFileLocator classFileLocator, TypePool.Default.ReaderMode readerMode) {
×
857
                this.classFileLocator = classFileLocator;
×
858
                this.readerMode = readerMode;
×
859
            }
×
860

861
            /**
862
             * Creates a new type pool resolver that supplements the supplied class loader to the implicit type pool.
863
             *
864
             * @param classLoader The class loader to use as a supplement which can be {@code null} to represent the bootstrap loader.
865
             * @return An appropriate type pool resolver.
866
             */
867
            public static TypePoolResolver of(@MaybeNull ClassLoader classLoader) {
868
                return new ForClassFileLocator(ClassFileLocator.ForClassLoader.of(classLoader));
×
869
            }
870

871
            /**
872
             * {@inheritDoc}
873
             */
874
            public TypePool resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
875
                return new TypePool.Default(new TypePool.CacheProvider.Simple(), classFileLocator, readerMode, typePool);
×
876
            }
877
        }
878
    }
879

880
    /**
881
     * A substitution replaces or enhances an interaction with a field or method within an instrumented method.
882
     */
883
    public interface Substitution {
884

885
        /**
886
         * Resolves this substitution into a stack manipulation.
887
         *
888
         * @param receiver          The target type on which a member is accessed.
889
         * @param original          The field, method or constructor that is substituted.
890
         * @param parameters        All parameters that serve as input to this access.
891
         * @param result            The result that is expected from the interaction or {@code void} if no result is expected.
892
         * @param methodHandle      A method handle describing the substituted expression.
893
         * @param stackManipulation The original byte code expression that is being executed.
894
         * @param freeOffset        The first free offset of the local variable array that can be used for storing values.
895
         * @return A stack manipulation that represents the access.
896
         */
897
        StackManipulation resolve(TypeDescription receiver,
898
                                  ByteCodeElement.Member original,
899
                                  TypeList.Generic parameters,
900
                                  TypeDescription.Generic result,
901
                                  JavaConstant.MethodHandle methodHandle,
902
                                  StackManipulation stackManipulation,
903
                                  int freeOffset);
904

905
        /**
906
         * A factory for creating a substitution for an instrumented method.
907
         */
908
        interface Factory {
909

910
            /**
911
             * Creates a substitution for an instrumented method.
912
             *
913
             * @param instrumentedType   The instrumented type.
914
             * @param instrumentedMethod The instrumented method.
915
             * @param typePool           The type pool being used.
916
             * @return The substitution to apply within the instrumented method.
917
             */
918
            Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool);
919
        }
920

921
        /**
922
         * A substitution that drops any field or method access and returns the expected return
923
         * type's default value, i.e {@code null} or zero for primitive types.
924
         */
925
        enum Stubbing implements Substitution, Factory {
1✔
926

927
            /**
928
             * The singleton instance.
929
             */
930
            INSTANCE;
1✔
931

932
            /**
933
             * {@inheritDoc}
934
             */
935
            public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
936
                return this;
1✔
937
            }
938

939
            /**
940
             * {@inheritDoc}
941
             */
942
            public StackManipulation resolve(TypeDescription receiver, ByteCodeElement.Member original, TypeList.Generic parameters, TypeDescription.Generic result, JavaConstant.MethodHandle methodHandle, StackManipulation stackManipulation, int freeOffset) {
943
                List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(parameters.size());
1✔
944
                for (int index = parameters.size() - 1; index >= 0; index--) {
1✔
945
                    stackManipulations.add(Removal.of(parameters.get(index)));
1✔
946
                }
947
                return new StackManipulation.Compound(CompoundList.of(stackManipulations, DefaultValue.of(result.asErasure())));
1✔
948
            }
949
        }
950

951
        /**
952
         * A substitution that loads a fixed value.
953
         */
954
        @HashCodeAndEqualsPlugin.Enhance
955
        class ForValue implements Substitution, Factory {
956

957
            /**
958
             * The stack manipulation to load the value that represents the substitution.
959
             */
960
            private final StackManipulation stackManipulation;
961

962
            /**
963
             * The type of the represented stack manipulation.
964
             */
965
            private final TypeDescription.Generic typeDescription;
966

967
            /**
968
             * Creates a new substitution for loading a constant value.
969
             *
970
             * @param stackManipulation The stack manipulation to load the value that represents the substitution.
971
             * @param typeDescription   The type of the represented stack manipulation.
972
             */
973
            public ForValue(StackManipulation stackManipulation, TypeDescription.Generic typeDescription) {
1✔
974
                this.stackManipulation = stackManipulation;
1✔
975
                this.typeDescription = typeDescription;
1✔
976
            }
1✔
977

978
            /**
979
             * {@inheritDoc}
980
             */
981
            public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
982
                return this;
1✔
983
            }
984

985
            /**
986
             * {@inheritDoc}
987
             */
988
            public StackManipulation resolve(TypeDescription receiver,
989
                                             ByteCodeElement.Member original,
990
                                             TypeList.Generic parameters,
991
                                             TypeDescription.Generic result,
992
                                             JavaConstant.MethodHandle methodHandle,
993
                                             StackManipulation stackManipulation,
994
                                             int freeOffset) {
995
                List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(parameters.size());
1✔
996
                for (int index = parameters.size() - 1; index >= 0; index--) {
1✔
997
                    stackManipulations.add(Removal.of(parameters.get(index)));
1✔
998
                }
999
                if (!typeDescription.asErasure().isAssignableTo(result.asErasure())) {
1✔
1000
                    throw new IllegalStateException("Cannot assign " + typeDescription + " to " + result);
×
1001
                }
1002
                return new StackManipulation.Compound(CompoundList.of(stackManipulations, this.stackManipulation));
1✔
1003
            }
1004
        }
1005

1006
        /**
1007
         * A substitution with a field access.
1008
         */
1009
        @HashCodeAndEqualsPlugin.Enhance
1010
        class ForFieldAccess implements Substitution {
1011

1012
            /**
1013
             * The instrumented type.
1014
             */
1015
            private final TypeDescription instrumentedType;
1016

1017
            /**
1018
             * A resolver to locate the field to access.
1019
             */
1020
            private final FieldResolver fieldResolver;
1021

1022
            /**
1023
             * Creates a new substitution with a field access.
1024
             *
1025
             * @param instrumentedType The instrumented type.
1026
             * @param fieldResolver    A resolver to locate the field to access.
1027
             */
1028
            public ForFieldAccess(TypeDescription instrumentedType, FieldResolver fieldResolver) {
1✔
1029
                this.instrumentedType = instrumentedType;
1✔
1030
                this.fieldResolver = fieldResolver;
1✔
1031
            }
1✔
1032

1033
            /**
1034
             * {@inheritDoc}
1035
             */
1036
            @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Assuming declaring type for type member.")
1037
            public StackManipulation resolve(TypeDescription receiver,
1038
                                             ByteCodeElement.Member original,
1039
                                             TypeList.Generic parameters,
1040
                                             TypeDescription.Generic result,
1041
                                             JavaConstant.MethodHandle methodHandle,
1042
                                             StackManipulation stackManipulation,
1043
                                             int freeOffset) {
1044
                FieldDescription fieldDescription = fieldResolver.resolve(receiver, original, parameters, result);
1✔
1045
                if (!fieldDescription.isAccessibleTo(instrumentedType)) {
1✔
1046
                    throw new IllegalStateException(instrumentedType + " cannot access " + fieldDescription);
1✔
1047
                } else if (result.represents(void.class)) {
1✔
1048
                    if (parameters.size() != (fieldDescription.isStatic() ? 1 : 2)) {
1✔
1049
                        throw new IllegalStateException("Cannot set " + fieldDescription + " with " + parameters);
1✔
1050
                    } else if (!fieldDescription.isStatic() && !parameters.get(0).asErasure().isAssignableTo(fieldDescription.getDeclaringType().asErasure())) {
1✔
1051
                        throw new IllegalStateException("Cannot set " + fieldDescription + " on " + parameters.get(0));
×
1052
                    } else if (!parameters.get(fieldDescription.isStatic() ? 0 : 1).asErasure().isAssignableTo(fieldDescription.getType().asErasure())) {
1✔
1053
                        throw new IllegalStateException("Cannot set " + fieldDescription + " to " + parameters.get(fieldDescription.isStatic() ? 0 : 1));
1✔
1054
                    }
1055
                    return FieldAccess.forField(fieldDescription).write();
1✔
1056
                } else {
1057
                    if (parameters.size() != (fieldDescription.isStatic() ? 0 : 1)) {
1✔
1058
                        throw new IllegalStateException("Cannot set " + fieldDescription + " with " + parameters);
1✔
1059
                    } else if (!fieldDescription.isStatic() && !parameters.get(0).asErasure().isAssignableTo(fieldDescription.getDeclaringType().asErasure())) {
1✔
1060
                        throw new IllegalStateException("Cannot get " + fieldDescription + " on " + parameters.get(0));
×
1061
                    } else if (!fieldDescription.getType().asErasure().isAssignableTo(result.asErasure())) {
1✔
1062
                        throw new IllegalStateException("Cannot get " + fieldDescription + " as " + result);
1✔
1063
                    }
1064
                    return FieldAccess.forField(fieldDescription).read();
1✔
1065
                }
1066
            }
1067

1068
            /**
1069
             * A method resolver for locating a field for a substitute.
1070
             */
1071
            public interface FieldResolver {
1072

1073
                /**
1074
                 * Resolves the field to substitute with.
1075
                 *
1076
                 * @param receiver   The target type on which a member is accessed.
1077
                 * @param original   The target field, method or constructor that is substituted,
1078
                 * @param parameters All parameters that serve as input to this access.
1079
                 * @param result     The result that is expected from the interaction or {@code void} if no result is expected.
1080
                 * @return The field to substitute with.
1081
                 */
1082
                FieldDescription resolve(TypeDescription receiver, ByteCodeElement.Member original, TypeList.Generic parameters, TypeDescription.Generic result);
1083

1084
                /**
1085
                 * A simple field resolver that returns a specific field.
1086
                 */
1087
                @HashCodeAndEqualsPlugin.Enhance
1088
                class Simple implements FieldResolver {
1089

1090
                    /**
1091
                     * The field to access.
1092
                     */
1093
                    private final FieldDescription fieldDescription;
1094

1095
                    /**
1096
                     * Creates a simple field resolver.
1097
                     *
1098
                     * @param fieldDescription The field to access.
1099
                     */
1100
                    public Simple(FieldDescription fieldDescription) {
1✔
1101
                        this.fieldDescription = fieldDescription;
1✔
1102
                    }
1✔
1103

1104
                    /**
1105
                     * {@inheritDoc}
1106
                     */
1107
                    public FieldDescription resolve(TypeDescription receiver, ByteCodeElement.Member original, TypeList.Generic parameters, TypeDescription.Generic result) {
1108
                        return fieldDescription;
1✔
1109
                    }
1110
                }
1111

1112
                /**
1113
                 * A field matcher that resolves a non-static field on the first parameter type of the substituted member usage.
1114
                 */
1115
                @HashCodeAndEqualsPlugin.Enhance
1116
                class ForElementMatcher implements FieldResolver {
1117

1118
                    /**
1119
                     * The instrumented type.
1120
                     */
1121
                    private final TypeDescription instrumentedType;
1122

1123
                    /**
1124
                     * The matcher to use for locating the field to substitute with.
1125
                     */
1126
                    private final ElementMatcher<? super FieldDescription> matcher;
1127

1128
                    /**
1129
                     * Creates a new field resolver that locates a field on the receiver type using a matcher.
1130
                     *
1131
                     * @param instrumentedType The instrumented type.
1132
                     * @param matcher          The matcher to use for locating the field to substitute with.
1133
                     */
1134
                    protected ForElementMatcher(TypeDescription instrumentedType, ElementMatcher<? super FieldDescription> matcher) {
1✔
1135
                        this.instrumentedType = instrumentedType;
1✔
1136
                        this.matcher = matcher;
1✔
1137
                    }
1✔
1138

1139
                    /**
1140
                     * {@inheritDoc}
1141
                     */
1142
                    public FieldDescription resolve(TypeDescription receiver, ByteCodeElement.Member original, TypeList.Generic parameters, TypeDescription.Generic result) {
1143
                        if (parameters.isEmpty()) {
1✔
1144
                            throw new IllegalStateException("Cannot substitute parameterless instruction with " + original);
1✔
1145
                        } else if (parameters.get(0).isPrimitive() || parameters.get(0).isArray()) {
1✔
1146
                            throw new IllegalStateException("Cannot access field on primitive or array type for " + original);
1✔
1147
                        }
1148
                        TypeDefinition current = parameters.get(0).accept(new TypeDescription.Generic.Visitor.Substitutor.ForReplacement(instrumentedType));
1✔
1149
                        do {
1150
                            FieldList<?> fields = current.getDeclaredFields().filter(not(isStatic()).<FieldDescription>and(isVisibleTo(instrumentedType)).and(matcher));
1✔
1151
                            if (fields.size() == 1) {
1✔
1152
                                return fields.getOnly();
1✔
1153
                            } else if (fields.size() > 1) {
1✔
1154
                                throw new IllegalStateException("Ambiguous field location of " + fields);
×
1155
                            }
1156
                            current = current.getSuperClass();
1✔
1157
                        } while (current != null);
1✔
1158
                        throw new IllegalStateException("Cannot locate field matching " + matcher + " on " + receiver);
1✔
1159
                    }
1160
                }
1161
            }
1162

1163
            /**
1164
             * A factory for a substitution that substitutes with a given field.
1165
             */
1166
            @HashCodeAndEqualsPlugin.Enhance
1167
            public static class OfGivenField implements Factory {
1168

1169
                /**
1170
                 * The field to substitute with.
1171
                 */
1172
                private final FieldDescription fieldDescription;
1173

1174
                /**
1175
                 * Creates a new factory that substitues with a given field.
1176
                 *
1177
                 * @param fieldDescription The field to substitute with.
1178
                 */
1179
                public OfGivenField(FieldDescription fieldDescription) {
1✔
1180
                    this.fieldDescription = fieldDescription;
1✔
1181
                }
1✔
1182

1183
                /**
1184
                 * {@inheritDoc}
1185
                 */
1186
                public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
1187
                    return new ForFieldAccess(instrumentedType, new FieldResolver.Simple(fieldDescription));
1✔
1188
                }
1189
            }
1190

1191
            /**
1192
             * A factory for a substitution that locates a field on the receiver type using a matcher.
1193
             */
1194
            @HashCodeAndEqualsPlugin.Enhance
1195
            public static class OfMatchedField implements Factory {
1196

1197
                /**
1198
                 * The matcher to apply.
1199
                 */
1200
                private final ElementMatcher<? super FieldDescription> matcher;
1201

1202
                /**
1203
                 * Creates a new substitution factory that locates a field by applying a matcher on the receiver type.
1204
                 *
1205
                 * @param matcher The matcher to apply.
1206
                 */
1207
                public OfMatchedField(ElementMatcher<? super FieldDescription> matcher) {
1✔
1208
                    this.matcher = matcher;
1✔
1209
                }
1✔
1210

1211
                /**
1212
                 * {@inheritDoc}
1213
                 */
1214
                public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
1215
                    return new ForFieldAccess(instrumentedType, new FieldResolver.ForElementMatcher(instrumentedType, matcher));
1✔
1216
                }
1217
            }
1218
        }
1219

1220
        /**
1221
         * A substitution with a method invocation.
1222
         */
1223
        @HashCodeAndEqualsPlugin.Enhance
1224
        class ForMethodInvocation implements Substitution {
1225

1226
            /**
1227
             * The instrumented type.
1228
             */
1229
            private final TypeDescription instrumentedType;
1230

1231
            /**
1232
             * The method resolver to use.
1233
             */
1234
            private final MethodResolver methodResolver;
1235

1236
            /**
1237
             * Creates a new method-resolving substitution.
1238
             *
1239
             * @param instrumentedType The instrumented type.
1240
             * @param methodResolver   The method resolver to use.
1241
             */
1242
            public ForMethodInvocation(TypeDescription instrumentedType, MethodResolver methodResolver) {
1✔
1243
                this.instrumentedType = instrumentedType;
1✔
1244
                this.methodResolver = methodResolver;
1✔
1245
            }
1✔
1246

1247
            /**
1248
             * {@inheritDoc}
1249
             */
1250
            public StackManipulation resolve(TypeDescription receiver,
1251
                                             ByteCodeElement.Member original,
1252
                                             TypeList.Generic parameters,
1253
                                             TypeDescription.Generic result,
1254
                                             JavaConstant.MethodHandle methodHandle,
1255
                                             StackManipulation stackManipulation,
1256
                                             int freeOffset) {
1257
                MethodDescription methodDescription = methodResolver.resolve(receiver, original, parameters, result);
1✔
1258
                if (!methodDescription.isAccessibleTo(instrumentedType)) {
1✔
1259
                    throw new IllegalStateException(instrumentedType + " cannot access " + methodDescription);
1✔
1260
                }
1261
                TypeList.Generic mapped = methodDescription.isStatic()
1✔
1262
                        ? methodDescription.getParameters().asTypeList()
1✔
1263
                        : new TypeList.Generic.Explicit(CompoundList.of(methodDescription.getDeclaringType(), methodDescription.getParameters().asTypeList()));
1✔
1264
                if (!methodDescription.getReturnType().asErasure().isAssignableTo(result.asErasure())) {
1✔
1265
                    throw new IllegalStateException("Cannot assign return value of " + methodDescription + " to " + result);
1✔
1266
                } else if (mapped.size() != parameters.size()) {
1✔
1267
                    throw new IllegalStateException("Cannot invoke " + methodDescription + " on " + parameters.size() + " parameters");
1✔
1268
                }
1269
                for (int index = 0; index < mapped.size(); index++) {
1✔
1270
                    if (!parameters.get(index).asErasure().isAssignableTo(mapped.get(index).asErasure())) {
1✔
1271
                        throw new IllegalStateException("Cannot invoke " + methodDescription + " on parameter " + index + " of type " + parameters.get(index));
1✔
1272
                    }
1273
                }
1274
                return methodDescription.isVirtual() ? MethodInvocation.invoke(methodDescription).virtual(mapped.get(THIS_REFERENCE).asErasure()) : MethodInvocation.invoke(methodDescription);
1✔
1275
            }
1276

1277
            /**
1278
             * A method resolver for locating a method for a substitute.
1279
             */
1280
            public interface MethodResolver {
1281

1282
                /**
1283
                 * Resolves the method to substitute with.
1284
                 *
1285
                 * @param receiver   The target type on which a member is accessed.
1286
                 * @param original   The target field, method or constructor that is substituted,
1287
                 * @param parameters All parameters that serve as input to this access.
1288
                 * @param result     The result that is expected from the interaction or {@code void} if no result is expected.
1289
                 * @return The field to substitute with.
1290
                 */
1291
                MethodDescription resolve(TypeDescription receiver, ByteCodeElement.Member original, TypeList.Generic parameters, TypeDescription.Generic result);
1292

1293
                /**
1294
                 * A simple method resolver that returns a given method.
1295
                 */
1296
                @HashCodeAndEqualsPlugin.Enhance
1297
                class Simple implements MethodResolver {
1298

1299
                    /**
1300
                     * The method to substitute with.
1301
                     */
1302
                    private final MethodDescription methodDescription;
1303

1304
                    /**
1305
                     * Creates a new simple method resolver.
1306
                     *
1307
                     * @param methodDescription The method to substitute with.
1308
                     */
1309
                    public Simple(MethodDescription methodDescription) {
1✔
1310
                        this.methodDescription = methodDescription;
1✔
1311
                    }
1✔
1312

1313
                    /**
1314
                     * {@inheritDoc}
1315
                     */
1316
                    public MethodDescription resolve(TypeDescription receiver, ByteCodeElement.Member original, TypeList.Generic parameters, TypeDescription.Generic result) {
1317
                        return methodDescription;
1✔
1318
                    }
1319
                }
1320

1321
                /**
1322
                 * A method resolver that locates a non-static method by locating it from the receiver type.
1323
                 */
1324
                @HashCodeAndEqualsPlugin.Enhance
1325
                class Matching implements MethodResolver {
1326

1327
                    /**
1328
                     * The instrumented type.
1329
                     */
1330
                    private final TypeDescription instrumentedType;
1331

1332
                    /**
1333
                     * The method graph compiler to use.
1334
                     */
1335
                    private final MethodGraph.Compiler methodGraphCompiler;
1336

1337
                    /**
1338
                     * The matcher to use for locating the method to substitute with.
1339
                     */
1340
                    private final ElementMatcher<? super MethodDescription> matcher;
1341

1342
                    /**
1343
                     * Creates a new matching method resolver.
1344
                     *
1345
                     * @param instrumentedType    The instrumented type.
1346
                     * @param methodGraphCompiler The method graph compiler to use.
1347
                     * @param matcher             The matcher to use for locating the method to substitute with.
1348
                     */
1349
                    public Matching(TypeDescription instrumentedType, MethodGraph.Compiler methodGraphCompiler, ElementMatcher<? super MethodDescription> matcher) {
1✔
1350
                        this.instrumentedType = instrumentedType;
1✔
1351
                        this.methodGraphCompiler = methodGraphCompiler;
1✔
1352
                        this.matcher = matcher;
1✔
1353
                    }
1✔
1354

1355
                    /**
1356
                     * {@inheritDoc}
1357
                     */
1358
                    public MethodDescription resolve(TypeDescription receiver, ByteCodeElement.Member original, TypeList.Generic parameters, TypeDescription.Generic result) {
1359
                        if (parameters.isEmpty()) {
1✔
1360
                            throw new IllegalStateException("Cannot substitute parameterless instruction with " + original);
1✔
1361
                        } else if (parameters.get(0).isPrimitive() || parameters.get(0).isArray()) {
1✔
1362
                            throw new IllegalStateException("Cannot invoke method on primitive or array type for " + original);
1✔
1363
                        }
1364
                        TypeDefinition typeDefinition = parameters.get(0).accept(new TypeDescription.Generic.Visitor.Substitutor.ForReplacement(instrumentedType));
1✔
1365
                        List<MethodDescription> candidates = CompoundList.<MethodDescription>of(methodGraphCompiler.compile(typeDefinition, instrumentedType).listNodes()
1✔
1366
                                .asMethodList()
1✔
1367
                                .filter(matcher), typeDefinition.getDeclaredMethods().filter(isPrivate().<MethodDescription>and(isVisibleTo(instrumentedType)).and(matcher)));
1✔
1368
                        if (candidates.size() == 1) {
1✔
1369
                            return candidates.get(0);
1✔
1370
                        } else {
1371
                            throw new IllegalStateException("Not exactly one method that matches " + matcher + ": " + candidates);
1✔
1372
                        }
1373
                    }
1374
                }
1375
            }
1376

1377
            /**
1378
             * A factory for a substitution that invokes the instrumented method.
1379
             */
1380
            enum OfInstrumentedMethod implements Factory {
1✔
1381

1382
                /**
1383
                 * The singleton instance.
1384
                 */
1385
                INSTANCE;
1✔
1386

1387
                /**
1388
                 * {@inheritDoc}
1389
                 */
1390
                public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
1391
                    return new ForMethodInvocation(instrumentedType, new MethodResolver.Simple(instrumentedMethod));
1✔
1392
                }
1393
            }
1394

1395
            /**
1396
             * A factory for a substitution that invokes a given method.
1397
             */
1398
            @HashCodeAndEqualsPlugin.Enhance
1399
            public static class OfGivenMethod implements Factory {
1400

1401
                /**
1402
                 * The method to invoke.
1403
                 */
1404
                private final MethodDescription methodDescription;
1405

1406
                /**
1407
                 * Creates a new factory for a substitution that invokes a given method.
1408
                 *
1409
                 * @param methodDescription The method to invoke.
1410
                 */
1411
                public OfGivenMethod(MethodDescription methodDescription) {
1✔
1412
                    this.methodDescription = methodDescription;
1✔
1413
                }
1✔
1414

1415
                /**
1416
                 * {@inheritDoc}
1417
                 */
1418
                public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
1419
                    return new ForMethodInvocation(instrumentedType, new MethodResolver.Simple(methodDescription));
1✔
1420
                }
1421
            }
1422

1423
            /**
1424
             * A factory for a substitution that locates a method on the receiver type using a matcher.
1425
             */
1426
            @HashCodeAndEqualsPlugin.Enhance
1427
            public static class OfMatchedMethod implements Factory {
1428

1429
                /**
1430
                 * The matcher for locating the method to substitute with.
1431
                 */
1432
                private final ElementMatcher<? super MethodDescription> matcher;
1433

1434
                /**
1435
                 * The method graph compiler to use.
1436
                 */
1437
                private final MethodGraph.Compiler methodGraphCompiler;
1438

1439
                /**
1440
                 * Creates a factory for a substitution that locates a method on the receiver type.
1441
                 *
1442
                 * @param matcher             The matcher for locating the method to substitute with.
1443
                 * @param methodGraphCompiler The method graph compiler to use.
1444
                 */
1445
                public OfMatchedMethod(ElementMatcher<? super MethodDescription> matcher, MethodGraph.Compiler methodGraphCompiler) {
1✔
1446
                    this.matcher = matcher;
1✔
1447
                    this.methodGraphCompiler = methodGraphCompiler;
1✔
1448
                }
1✔
1449

1450
                /**
1451
                 * {@inheritDoc}
1452
                 */
1453
                public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
1454
                    return new ForMethodInvocation(instrumentedType, new MethodResolver.Matching(instrumentedType, methodGraphCompiler, matcher));
1✔
1455
                }
1456
            }
1457
        }
1458

1459
        /**
1460
         * A substitution chain allows for chaining multiple substitution steps for a byte code element being replaced.
1461
         */
1462
        @HashCodeAndEqualsPlugin.Enhance
1463
        class Chain implements Substitution {
1464

1465
            /**
1466
             * The assigner to use.
1467
             */
1468
            private final Assigner assigner;
1469

1470
            /**
1471
             * The typing of the assignment to use.
1472
             */
1473
            private final Assigner.Typing typing;
1474

1475
            /**
1476
             * The substitution steps to apply.
1477
             */
1478
            private final List<Step> steps;
1479

1480
            /**
1481
             * Creates a new substitution chain.
1482
             *
1483
             * @param assigner The assigner to use.
1484
             * @param typing   The typing of the assignment to use.
1485
             * @param steps    The substitution steps to apply.
1486
             */
1487
            protected Chain(Assigner assigner, Assigner.Typing typing, List<Step> steps) {
1✔
1488
                this.assigner = assigner;
1✔
1489
                this.typing = typing;
1✔
1490
                this.steps = steps;
1✔
1491
            }
1✔
1492

1493
            /**
1494
             * Creates a new substitution chain that uses a default assigner and static typing.
1495
             *
1496
             * @return A new substitution chain.
1497
             */
1498
            public static Chain.Factory withDefaultAssigner() {
1499
                return with(Assigner.DEFAULT, Assigner.Typing.STATIC);
1✔
1500
            }
1501

1502
            /**
1503
             * Creates a new substitution chain.
1504
             *
1505
             * @param assigner The assigner to use.
1506
             * @param typing   The typing of the assignment to use.
1507
             * @return A new substitution chain.
1508
             */
1509
            public static Chain.Factory with(Assigner assigner, Assigner.Typing typing) {
1510
                return new Chain.Factory(assigner, typing, Collections.<Step.Factory>emptyList());
1✔
1511
            }
1512

1513
            /**
1514
             * {@inheritDoc}
1515
             */
1516
            public StackManipulation resolve(TypeDescription receiver,
1517
                                             ByteCodeElement.Member original,
1518
                                             TypeList.Generic parameters,
1519
                                             TypeDescription.Generic result,
1520
                                             JavaConstant.MethodHandle methodHandle,
1521
                                             StackManipulation stackManipulation,
1522
                                             int freeOffset) {
1523
                List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(1
1✔
1524
                        + parameters.size() + steps.size() * 2
1✔
1525
                        + (result.represents(void.class) ? 0 : 2));
1✔
1526
                Map<Integer, Integer> offsets = new HashMap<Integer, Integer>();
1✔
1527
                for (int index = parameters.size() - 1; index >= 0; index--) {
1✔
1528
                    stackManipulations.add(MethodVariableAccess.of(parameters.get(index)).storeAt(freeOffset));
1✔
1529
                    offsets.put(index, freeOffset);
1✔
1530
                    freeOffset += parameters.get(index).getStackSize().getSize();
1✔
1531
                }
1532
                stackManipulations.add(DefaultValue.of(result));
1✔
1533
                TypeDescription.Generic current = result;
1✔
1534
                for (Step step : steps) {
1✔
1535
                    Step.Resolution resolution = step.resolve(receiver, original, parameters, result, methodHandle, stackManipulation, current, offsets, freeOffset);
1✔
1536
                    stackManipulations.add(resolution.getStackManipulation());
1✔
1537
                    current = resolution.getResultType();
1✔
1538
                }
1✔
1539
                StackManipulation assignment = assigner.assign(current, result, typing);
1✔
1540
                if (!assignment.isValid()) {
1✔
1541
                    throw new IllegalStateException("Failed to assign " + current + " to " + result);
×
1542
                }
1543
                stackManipulations.add(assignment);
1✔
1544
                return new StackManipulation.Compound(stackManipulations);
1✔
1545
            }
1546

1547
            /**
1548
             * Represents a step of a substitution chain.
1549
             */
1550
            public interface Step {
1551

1552
                /**
1553
                 * Resolves this step of a substitution chain.
1554
                 *
1555
                 * @param receiver          The target result type of the substitution.
1556
                 * @param original          The byte code element that is currently substituted.
1557
                 * @param parameters        The parameters of the substituted element.
1558
                 * @param result            The resulting type of the substituted element.
1559
                 * @param methodHandle      A method handle of the stackManipulation invocation that is being substituted.
1560
                 * @param stackManipulation The byte code instruction that is being substituted.
1561
                 * @param current           The current type of the applied substitution that is the top element on the operand stack.
1562
                 * @param offsets           The arguments of the substituted byte code element mapped to their local variable offsets.
1563
                 * @param freeOffset        The first free offset in the local variable array.
1564
                 * @return A resolved substitution step for the supplied inputs.
1565
                 */
1566
                Resolution resolve(TypeDescription receiver,
1567
                                   ByteCodeElement.Member original,
1568
                                   TypeList.Generic parameters,
1569
                                   TypeDescription.Generic result,
1570
                                   JavaConstant.MethodHandle methodHandle,
1571
                                   StackManipulation stackManipulation,
1572
                                   TypeDescription.Generic current,
1573
                                   Map<Integer, Integer> offsets,
1574
                                   int freeOffset);
1575

1576
                /**
1577
                 * A resolved substitution step.
1578
                 */
1579
                interface Resolution {
1580

1581
                    /**
1582
                     * Returns the stack manipulation to apply the substitution.
1583
                     *
1584
                     * @return The stack manipulation to apply the substitution.
1585
                     */
1586
                    StackManipulation getStackManipulation();
1587

1588
                    /**
1589
                     * Returns the resulting type of the substitution or {@code void} if no resulting value is applied.
1590
                     *
1591
                     * @return The resulting type of the substitution or {@code void} if no resulting value is applied.
1592
                     */
1593
                    TypeDescription.Generic getResultType();
1594
                }
1595

1596
                /**
1597
                 * Resolves a substitution for an instrumented method.
1598
                 */
1599
                interface Factory {
1600

1601
                    /**
1602
                     * Creates a substitution step for an instrumented method.
1603
                     *
1604
                     * @param assigner           The assigner to use.
1605
                     * @param typing             The typing to use.
1606
                     * @param instrumentedType   The instrumented type.
1607
                     * @param instrumentedMethod The instrumented method.
1608
                     * @return The substitution step to apply.
1609
                     */
1610
                    Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod);
1611
                }
1612

1613
                /**
1614
                 * A step that executes the original method invocation or field access.
1615
                 */
1616
                enum OfOriginalExpression implements Step, Factory {
1✔
1617

1618
                    /**
1619
                     * The singleton instance.
1620
                     */
1621
                    INSTANCE;
1✔
1622

1623
                    /**
1624
                     * {@inheritDoc}
1625
                     */
1626
                    public Resolution resolve(TypeDescription receiver,
1627
                                              ByteCodeElement.Member original,
1628
                                              TypeList.Generic parameters,
1629
                                              TypeDescription.Generic result,
1630
                                              JavaConstant.MethodHandle methodHandle,
1631
                                              StackManipulation stackManipulation,
1632
                                              TypeDescription.Generic current,
1633
                                              Map<Integer, Integer> offsets,
1634
                                              int freeOffset) {
1635
                        List<StackManipulation> stackManipulations;
1636
                        if (original instanceof MethodDescription && ((MethodDescription) original).isConstructor()) {
1✔
1637
                            stackManipulations = new ArrayList<StackManipulation>(parameters.size() + 4);
1✔
1638
                            stackManipulations.add(Removal.of(current));
1✔
1639
                            stackManipulations.add(TypeCreation.of(original.getDeclaringType().asErasure()));
1✔
1640
                            stackManipulations.add(Duplication.SINGLE);
1✔
1641
                        } else {
1642
                            stackManipulations = new ArrayList<StackManipulation>(parameters.size() + 4);
1✔
1643
                            stackManipulations.add(Removal.of(current));
1✔
1644
                        }
1645
                        for (int index = 0; index < parameters.size(); index++) {
1✔
1646
                            stackManipulations.add(MethodVariableAccess.of(parameters.get(index)).loadFrom(offsets.get(index)));
1✔
1647
                        }
1648
                        if (original instanceof MethodDescription) {
1✔
1649
                            stackManipulations.add(stackManipulation);
1✔
1650
                            return new Simple(new StackManipulation.Compound(stackManipulations), ((MethodDescription) original).isConstructor()
1✔
1651
                                    ? original.getDeclaringType().asGenericType()
1✔
1652
                                    : ((MethodDescription) original).getReturnType());
1✔
1653
                        } else if (original instanceof FieldDescription) {
1✔
1654
                            if (original.isStatic()) {
1✔
1655
                                if (parameters.isEmpty()) {
1✔
1656
                                    stackManipulations.add(stackManipulation);
1✔
1657
                                    return new Simple(new StackManipulation.Compound(stackManipulations), ((FieldDescription) original).getType());
1✔
1658
                                } else /* if (parameters.size() == 1) */ {
1659
                                    stackManipulations.add(stackManipulation);
1✔
1660
                                    return new Simple(new StackManipulation.Compound(stackManipulations), TypeDefinition.Sort.describe(void.class));
1✔
1661
                                }
1662
                            } else {
1663
                                if (parameters.size() == 1) {
1✔
1664
                                    stackManipulations.add(FieldAccess.forField((FieldDescription) original).read());
1✔
1665
                                    return new Simple(new StackManipulation.Compound(stackManipulations), ((FieldDescription) original).getType());
1✔
1666
                                } else /* if (parameters.size() == 2) */ {
1667
                                    stackManipulations.add(FieldAccess.forField((FieldDescription) original).write());
1✔
1668
                                    return new Simple(new StackManipulation.Compound(stackManipulations), TypeDefinition.Sort.describe(void.class));
1✔
1669
                                }
1670
                            }
1671
                        } else {
1672
                            throw new IllegalArgumentException("Unexpected target type: " + original);
×
1673
                        }
1674
                    }
1675

1676
                    /**
1677
                     * {@inheritDoc}
1678
                     */
1679
                    public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
1680
                        return this;
1✔
1681
                    }
1682
                }
1683

1684
                /**
1685
                 * A simple substitution step within a substitution chain.
1686
                 */
1687
                @HashCodeAndEqualsPlugin.Enhance
1688
                class Simple implements Step, Resolution, Factory {
1689

1690
                    /**
1691
                     * The stack manipulation to apply.
1692
                     */
1693
                    private final StackManipulation stackManipulation;
1694

1695
                    /**
1696
                     * The resulting type of applying the stack manipulation.
1697
                     */
1698
                    private final TypeDescription.Generic resultType;
1699

1700
                    /**
1701
                     * Creates a new simple substitution step.
1702
                     *
1703
                     * @param stackManipulation The stack manipulation to apply.
1704
                     * @param resultType        The resulting type of applying the stack manipulation.
1705
                     */
1706
                    public Simple(StackManipulation stackManipulation, Type resultType) {
1707
                        this(stackManipulation, TypeDefinition.Sort.describe(resultType));
1✔
1708
                    }
1✔
1709

1710
                    /**
1711
                     * Creates a new simple substitution step.
1712
                     *
1713
                     * @param stackManipulation The stack manipulation to apply.
1714
                     * @param resultType        The resulting type of applying the stack manipulation.
1715
                     */
1716
                    public Simple(StackManipulation stackManipulation, TypeDescription.Generic resultType) {
1✔
1717
                        this.stackManipulation = stackManipulation;
1✔
1718
                        this.resultType = resultType;
1✔
1719
                    }
1✔
1720

1721
                    /**
1722
                     * Resolves a compile-time constant as the next step value.
1723
                     *
1724
                     * @param value The compile-time constant to resolve.
1725
                     * @return An appropriate step factory.
1726
                     */
1727
                    public static Step.Factory of(Object value) {
1728
                        ConstantValue constant = ConstantValue.Simple.wrap(value);
1✔
1729
                        return new Simple(constant.toStackManipulation(), constant.getTypeDescription().asGenericType());
1✔
1730
                    }
1731

1732
                    /**
1733
                     * {@inheritDoc}
1734
                     */
1735
                    public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
1736
                        return this;
1✔
1737
                    }
1738

1739
                    /**
1740
                     * {@inheritDoc}
1741
                     */
1742
                    public Resolution resolve(TypeDescription receiver,
1743
                                              ByteCodeElement.Member original,
1744
                                              TypeList.Generic parameters,
1745
                                              TypeDescription.Generic result,
1746
                                              JavaConstant.MethodHandle methodHandle,
1747
                                              StackManipulation stackManipulation,
1748
                                              TypeDescription.Generic current,
1749
                                              Map<Integer, Integer> offsets,
1750
                                              int freeOffset) {
1751
                        return receiver.represents(void.class)
1✔
1752
                                ? this
1753
                                : new Simple(new StackManipulation.Compound(Removal.of(current), this.stackManipulation), resultType);
1✔
1754
                    }
1755

1756
                    /**
1757
                     * {@inheritDoc}
1758
                     */
1759
                    public StackManipulation getStackManipulation() {
1760
                        return stackManipulation;
1✔
1761
                    }
1762

1763
                    /**
1764
                     * {@inheritDoc}
1765
                     */
1766
                    public TypeDescription.Generic getResultType() {
1767
                        return resultType;
1✔
1768
                    }
1769
                }
1770

1771
                /**
1772
                 * A step within a substitution chain that converts the current type to the expected return type.
1773
                 */
1774
                @HashCodeAndEqualsPlugin.Enhance
1775
                class ForAssignment implements Step {
1776

1777
                    /**
1778
                     * The result type or {@code null} if the type of the substitution result should be targeted.
1779
                     */
1780
                    @MaybeNull
1781
                    @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1782
                    private final TypeDescription.Generic result;
1783

1784
                    /**
1785
                     * The assigner to use.
1786
                     */
1787
                    private final Assigner assigner;
1788

1789
                    /**
1790
                     * Creates a step for a type assignment.
1791
                     *
1792
                     * @param result   The result type or {@code null} if the type of the substitution result should be targeted.
1793
                     * @param assigner The assigner to use.
1794
                     */
1795
                    protected ForAssignment(@MaybeNull TypeDescription.Generic result, Assigner assigner) {
1✔
1796
                        this.result = result;
1✔
1797
                        this.assigner = assigner;
1✔
1798
                    }
1✔
1799

1800
                    /**
1801
                     * Creates a step factory that casts the current stack top value to the specified type.
1802
                     *
1803
                     * @param type The type that should be cast to.
1804
                     * @return An appropriate step factory.
1805
                     */
1806
                    public static Step.Factory castTo(Type type) {
1807
                        return new Factory(TypeDefinition.Sort.describe(type));
1✔
1808
                    }
1809

1810
                    /**
1811
                     * Creates a step factory that casts the current stack top value to the specified type.
1812
                     *
1813
                     * @param typeDescription The description of the type that should be cast to.
1814
                     * @return An appropriate step factory.
1815
                     */
1816
                    public static Step.Factory castTo(TypeDescription.Generic typeDescription) {
1817
                        return new Factory(typeDescription);
×
1818
                    }
1819

1820
                    /**
1821
                     * Creates a step factory that casts the current stack top value to the expected return value.
1822
                     *
1823
                     * @return An appropriate step factory.
1824
                     */
1825
                    public static Step.Factory castToSubstitutionResult() {
1826
                        return new Factory(null);
1✔
1827
                    }
1828

1829
                    /**
1830
                     * {@inheritDoc}
1831
                     */
1832
                    public Resolution resolve(TypeDescription receiver,
1833
                                              ByteCodeElement.Member original,
1834
                                              TypeList.Generic parameters,
1835
                                              TypeDescription.Generic result,
1836
                                              JavaConstant.MethodHandle methodHandle,
1837
                                              StackManipulation stackManipulation,
1838
                                              TypeDescription.Generic current,
1839
                                              Map<Integer, Integer> offsets,
1840
                                              int freeOffset) {
1841
                        StackManipulation assignment = assigner.assign(current, this.result == null ? result : this.result, Assigner.Typing.DYNAMIC);
1✔
1842
                        if (!assignment.isValid()) {
1✔
1843
                            throw new IllegalStateException("Failed to assign " + current + " to " + (this.result == null ? result : this.result));
×
1844
                        }
1845
                        return new Simple(assignment, this.result == null ? result : this.result);
1✔
1846
                    }
1847

1848
                    /**
1849
                     * A factory for creating a step for a dynamic type assignment.
1850
                     */
1851
                    @HashCodeAndEqualsPlugin.Enhance
1852
                    protected static class Factory implements Step.Factory {
1853

1854
                        /**
1855
                         * The result type or {@code null} if the type of the substitution result should be targeted.
1856
                         */
1857
                        @MaybeNull
1858
                        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1859
                        private final TypeDescription.Generic result;
1860

1861
                        /**
1862
                         * Creates a new factory for a step that applies a type assignment.
1863
                         *
1864
                         * @param result The result type or {@code null} if the type of the substitution result should be targeted.
1865
                         */
1866
                        protected Factory(@MaybeNull TypeDescription.Generic result) {
1✔
1867
                            this.result = result;
1✔
1868
                        }
1✔
1869

1870
                        /**
1871
                         * {@inheritDoc}
1872
                         */
1873
                        public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
1874
                            return new ForAssignment(result, assigner);
1✔
1875
                        }
1876
                    }
1877
                }
1878

1879
                /**
1880
                 * A step that substitutes an argument of a given index with a compatible type.
1881
                 */
1882
                @HashCodeAndEqualsPlugin.Enhance
1883
                class ForArgumentSubstitution implements Step {
1884

1885
                    /**
1886
                     * The stack manipulation that loads the substituted argument.
1887
                     */
1888
                    private final StackManipulation substitution;
1889

1890
                    /**
1891
                     * The type of the substituted argument.
1892
                     */
1893
                    private final TypeDescription.Generic typeDescription;
1894

1895
                    /**
1896
                     * The index of the argument to substitute.
1897
                     */
1898
                    private final int index;
1899

1900
                    /**
1901
                     * The assigner to use for assigning the argument.
1902
                     */
1903
                    private final Assigner assigner;
1904

1905
                    /**
1906
                     * The typing to use for the argument assignment.
1907
                     */
1908
                    private final Assigner.Typing typing;
1909

1910
                    /**
1911
                     * Creates an argument substitution step.
1912
                     *
1913
                     * @param substitution    The stack manipulation that loads the substituted argument.
1914
                     * @param typeDescription The type of the substituted argument.
1915
                     * @param index           The index of the argument to substitute.
1916
                     * @param assigner        The assigner to use for assigning the argument.
1917
                     * @param typing          The typing to use for the argument assignment.
1918
                     */
1919
                    protected ForArgumentSubstitution(StackManipulation substitution, TypeDescription.Generic typeDescription, int index, Assigner assigner, Assigner.Typing typing) {
1✔
1920
                        this.substitution = substitution;
1✔
1921
                        this.typeDescription = typeDescription;
1✔
1922
                        this.index = index;
1✔
1923
                        this.assigner = assigner;
1✔
1924
                        this.typing = typing;
1✔
1925
                    }
1✔
1926

1927
                    /**
1928
                     * Resolves a step substitution factory for a compile-time constant to replace an argument value at a given index.
1929
                     *
1930
                     * @param value The compile-time constant to replace.
1931
                     * @param index The index of the substituted argument.
1932
                     * @return An appropriate step factory.
1933
                     */
1934
                    public static Step.Factory of(Object value, int index) {
1935
                        if (index < 0) {
1✔
1936
                            throw new IllegalArgumentException("Index cannot be negative: " + index);
1✔
1937
                        }
1938
                        ConstantValue constant = ConstantValue.Simple.wrap(value);
1✔
1939
                        return new Factory(constant.toStackManipulation(), constant.getTypeDescription().asGenericType(), index);
1✔
1940
                    }
1941

1942
                    /**
1943
                     * {@inheritDoc}
1944
                     */
1945
                    public Resolution resolve(TypeDescription receiver,
1946
                                              ByteCodeElement.Member original,
1947
                                              TypeList.Generic parameters,
1948
                                              TypeDescription.Generic result,
1949
                                              JavaConstant.MethodHandle methodHandle,
1950
                                              StackManipulation stackManipulation,
1951
                                              TypeDescription.Generic current,
1952
                                              Map<Integer, Integer> offsets,
1953
                                              int freeOffset) {
1954
                        if (index >= parameters.size()) {
1✔
1955
                            throw new IllegalStateException(original + " has not " + index + " arguments");
×
1956
                        }
1957
                        StackManipulation assignment = assigner.assign(typeDescription, parameters.get(index), typing);
1✔
1958
                        if (!assignment.isValid()) {
1✔
1959
                            throw new IllegalStateException("Cannot assign " + typeDescription + " to " + parameters.get(index));
×
1960
                        }
1961
                        return new Simple(new StackManipulation.Compound(substitution, assignment, MethodVariableAccess.of(parameters.get(index)).storeAt(offsets.get(index))), current);
1✔
1962
                    }
1963

1964
                    /**
1965
                     * A factory to create an argument substitution step.
1966
                     */
1967
                    @HashCodeAndEqualsPlugin.Enhance
1968
                    public static class Factory implements Step.Factory {
1969

1970
                        /**
1971
                         * The stack manipulation that loads the substituted argument.
1972
                         */
1973
                        private final StackManipulation stackManipulation;
1974

1975
                        /**
1976
                         * The type of the substituted argument.
1977
                         */
1978
                        private final TypeDescription.Generic typeDescription;
1979

1980
                        /**
1981
                         * The index of the argument to substitute.
1982
                         */
1983
                        private final int index;
1984

1985
                        /**
1986
                         * Creates a factory for an argument substitution step.
1987
                         *
1988
                         * @param stackManipulation The stack manipulation that loads the substituted argument.
1989
                         * @param type              The type of the substituted argument.
1990
                         * @param index             The index of the argument to substitute.
1991
                         */
1992
                        public Factory(StackManipulation stackManipulation, Type type, int index) {
1993
                            this(stackManipulation, TypeDefinition.Sort.describe(type), index);
×
1994
                        }
×
1995

1996
                        /**
1997
                         * Creates a factory for an argument substitution step.
1998
                         *
1999
                         * @param stackManipulation The stack manipulation that loads the substituted argument.
2000
                         * @param typeDescription   The type of the substituted argument.
2001
                         * @param index             The index of the argument to substitute.
2002
                         */
2003
                        public Factory(StackManipulation stackManipulation, TypeDescription.Generic typeDescription, int index) {
1✔
2004
                            this.stackManipulation = stackManipulation;
1✔
2005
                            this.typeDescription = typeDescription;
1✔
2006
                            this.index = index;
1✔
2007
                        }
1✔
2008

2009
                        /**
2010
                         * {@inheritDoc}
2011
                         */
2012
                        public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
2013
                            return new ForArgumentSubstitution(stackManipulation, typeDescription, index, assigner, typing);
1✔
2014
                        }
2015
                    }
2016
                }
2017

2018
                /**
2019
                 * A step that loads an argument to a method as the current chain value.
2020
                 */
2021
                @HashCodeAndEqualsPlugin.Enhance
2022
                class ForArgumentLoading implements Step, Factory {
2023

2024
                    /**
2025
                     * The index of the argument to substitute.
2026
                     */
2027
                    private final int index;
2028

2029
                    /**
2030
                     * Creates an argument loading step.
2031
                     *
2032
                     * @param index The index of the argument to load.
2033
                     */
2034
                    protected ForArgumentLoading(int index) {
1✔
2035
                        this.index = index;
1✔
2036
                    }
1✔
2037

2038
                    /**
2039
                     * Creates a factory that loads the argument for the targeted value's parameter of the specified index.
2040
                     *
2041
                     * @param index The index to load.
2042
                     * @return An appropriate factory.
2043
                     */
2044
                    public static Factory ofTarget(int index) {
2045
                        if (index < 0) {
1✔
2046
                            throw new IllegalArgumentException("Argument index cannot be negative: " + index);
1✔
2047
                        }
2048
                        return new ForArgumentLoading(index);
1✔
2049
                    }
2050

2051
                    /**
2052
                     * Creates a factory that loads the argument for the instrumented method's parameter of the specified index.
2053
                     *
2054
                     * @param index The index to load.
2055
                     * @return An appropriate factory.
2056
                     */
2057
                    public static Factory ofInstrumentedMethod(int index) {
2058
                        if (index < 0) {
1✔
2059
                            throw new IllegalArgumentException("Argument index cannot be negative: " + index);
1✔
2060
                        }
2061
                        return new OfInstrumentedMethod(index);
1✔
2062
                    }
2063

2064
                    /**
2065
                     * Creates a factory that loads the {@code this} reference of the instrumented method.
2066
                     *
2067
                     * @return An appropriate factory.
2068
                     */
2069
                    public static Factory ofThis() {
2070
                        return OfInstrumentedMethodThis.INSTANCE;
1✔
2071
                    }
2072

2073
                    /**
2074
                     * {@inheritDoc}
2075
                     */
2076
                    public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
2077
                        return this;
1✔
2078
                    }
2079

2080
                    /**
2081
                     * {@inheritDoc}
2082
                     */
2083
                    public Resolution resolve(TypeDescription receiver,
2084
                                              ByteCodeElement.Member original,
2085
                                              TypeList.Generic parameters,
2086
                                              TypeDescription.Generic result,
2087
                                              JavaConstant.MethodHandle methodHandle,
2088
                                              StackManipulation stackManipulation,
2089
                                              TypeDescription.Generic current,
2090
                                              Map<Integer, Integer> offsets,
2091
                                              int freeOffset) {
2092
                        if (index >= parameters.size()) {
1✔
2093
                            throw new IllegalStateException(original + " has not " + index + " arguments");
×
2094
                        }
2095
                        return new Simple(new StackManipulation.Compound(Removal.of(current), MethodVariableAccess.of(parameters.get(index)).loadFrom(offsets.get(index))), parameters.get(index));
1✔
2096
                    }
2097

2098
                    /**
2099
                     * A factory that resolves the {@code this} reference of the instrumented method.
2100
                     */
2101
                    protected enum OfInstrumentedMethodThis implements Factory {
1✔
2102

2103
                        /**
2104
                         * The singleton instance.
2105
                         */
2106
                        INSTANCE;
1✔
2107

2108
                        /**
2109
                         * {@inheritDoc}
2110
                         */
2111
                        public Step make(Assigner assigner,
2112
                                         Assigner.Typing typing,
2113
                                         TypeDescription instrumentedType,
2114
                                         MethodDescription instrumentedMethod) {
2115
                            if (instrumentedMethod.isStatic()) {
1✔
2116
                                throw new IllegalStateException(instrumentedMethod + " is static and does not define a this reference");
×
2117
                            }
2118
                            return new Simple(MethodVariableAccess.loadThis(), instrumentedType.asGenericType());
1✔
2119
                        }
2120
                    }
2121

2122
                    /**
2123
                     * A factory that resolves a given argument of the instrumented method.
2124
                     */
2125
                    @HashCodeAndEqualsPlugin.Enhance
2126
                    protected static class OfInstrumentedMethod implements Factory {
2127

2128
                        /**
2129
                         * The index of the argument to load.
2130
                         */
2131
                        private final int index;
2132

2133
                        /**
2134
                         * Creates a new factory for resolving an argument of the instrumented method.
2135
                         *
2136
                         * @param index The index of the argument to load.
2137
                         */
2138
                        protected OfInstrumentedMethod(int index) {
1✔
2139
                            this.index = index;
1✔
2140
                        }
1✔
2141

2142
                        /**
2143
                         * {@inheritDoc}
2144
                         */
2145
                        public Step make(Assigner assigner,
2146
                                         Assigner.Typing typing,
2147
                                         TypeDescription instrumentedType,
2148
                                         MethodDescription instrumentedMethod) {
2149
                            if (instrumentedMethod.getParameters().size() < index) {
1✔
2150
                                throw new IllegalStateException(instrumentedMethod + " does not declare " + index + " parameters");
×
2151
                            }
2152
                            ParameterDescription parameterDescription = instrumentedMethod.getParameters().get(index);
1✔
2153
                            return new Simple(MethodVariableAccess.load(parameterDescription), parameterDescription.getType());
1✔
2154
                        }
2155
                    }
2156
                }
2157

2158
                /**
2159
                 * Creates a step for a field access.
2160
                 */
2161
                @HashCodeAndEqualsPlugin.Enhance
2162
                abstract class ForField implements Step {
2163

2164
                    /**
2165
                     * The field description accessed in this step.
2166
                     */
2167
                    protected final FieldDescription fieldDescription;
2168

2169
                    /**
2170
                     * The assigner to use.
2171
                     */
2172
                    protected final Assigner assigner;
2173

2174
                    /**
2175
                     * The typing to use when assigning.
2176
                     */
2177
                    protected final Assigner.Typing typing;
2178

2179
                    /**
2180
                     * Creates a new step for a field access.
2181
                     *
2182
                     * @param fieldDescription The field description accessed in this step.
2183
                     * @param assigner         The assigner to use.
2184
                     * @param typing           The typing to use when assigning.
2185
                     */
2186
                    protected ForField(FieldDescription fieldDescription, Assigner assigner, Assigner.Typing typing) {
1✔
2187
                        this.fieldDescription = fieldDescription;
1✔
2188
                        this.assigner = assigner;
1✔
2189
                        this.typing = typing;
1✔
2190
                    }
1✔
2191

2192
                    /**
2193
                     * {@inheritDoc}
2194
                     */
2195
                    @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Field description always has declaring type.")
2196
                    public Resolution resolve(TypeDescription receiver,
2197
                                              ByteCodeElement.Member original,
2198
                                              TypeList.Generic parameters,
2199
                                              TypeDescription.Generic result,
2200
                                              JavaConstant.MethodHandle methodHandle,
2201
                                              StackManipulation stackManipulation,
2202
                                              TypeDescription.Generic current,
2203
                                              Map<Integer, Integer> offsets,
2204
                                              int freeOffset) {
2205
                        List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(2);
1✔
2206
                        if (fieldDescription.isStatic()) {
1✔
2207
                            stackManipulations.add(Removal.of(current));
1✔
2208
                        } else {
2209
                            StackManipulation assignment = assigner.assign(current, fieldDescription.getDeclaringType().asGenericType(), typing);
1✔
2210
                            if (!assignment.isValid()) {
1✔
2211
                                throw new IllegalStateException("Cannot assign " + current + " to " + fieldDescription.getDeclaringType());
×
2212
                            }
2213
                            stackManipulations.add(assignment);
1✔
2214
                        }
2215
                        return doResolve(original, parameters, offsets, new StackManipulation.Compound(stackManipulations));
1✔
2216
                    }
2217

2218
                    /**
2219
                     * Completes the resolution.
2220
                     *
2221
                     * @param original          The byte code element that is currently substituted.
2222
                     * @param parameters        The parameters of the substituted element.
2223
                     * @param offsets           The arguments of the substituted byte code element mapped to their local variable offsets.
2224
                     * @param stackManipulation A stack manipulation to prepare the field access.
2225
                     * @return A resolved substitution step for the supplied inputs.
2226
                     */
2227
                    protected abstract Resolution doResolve(ByteCodeElement.Member original, TypeList.Generic parameters, Map<Integer, Integer> offsets, StackManipulation stackManipulation);
2228

2229
                    /**
2230
                     * A step for reading a field.
2231
                     */
2232
                    @HashCodeAndEqualsPlugin.Enhance
2233
                    public static class Read extends ForField {
2234

2235
                        /**
2236
                         * Creates a step for reading a field.
2237
                         *
2238
                         * @param fieldDescription A description of the field being read.
2239
                         * @param assigner         The assigner to use.
2240
                         * @param typing           The typing to use when assigning.
2241
                         */
2242
                        protected Read(FieldDescription fieldDescription, Assigner assigner, Assigner.Typing typing) {
2243
                            super(fieldDescription, assigner, typing);
1✔
2244
                        }
1✔
2245

2246
                        /**
2247
                         * {@inheritDoc}
2248
                         */
2249
                        protected Resolution doResolve(ByteCodeElement.Member original, TypeList.Generic parameters, Map<Integer, Integer> offsets, StackManipulation stackManipulation) {
2250
                            return new Simple(new StackManipulation.Compound(stackManipulation, FieldAccess.forField(fieldDescription).read()), fieldDescription.getType());
1✔
2251
                        }
2252

2253
                        /**
2254
                         * A factory for creating a field read step in a chain.
2255
                         */
2256
                        @HashCodeAndEqualsPlugin.Enhance
2257
                        public static class Factory implements Step.Factory {
2258

2259
                            /**
2260
                             * A description of the field being read.
2261
                             */
2262
                            private final FieldDescription fieldDescription;
2263

2264
                            /**
2265
                             * Creates a factory for a step reading a field.
2266
                             *
2267
                             * @param field The field being read.
2268
                             */
2269
                            public Factory(Field field) {
2270
                                this(new FieldDescription.ForLoadedField(field));
1✔
2271
                            }
1✔
2272

2273
                            /**
2274
                             * Creates a factory for a step reading a field.
2275
                             *
2276
                             * @param fieldDescription A description of the field being read.
2277
                             */
2278
                            public Factory(FieldDescription fieldDescription) {
1✔
2279
                                this.fieldDescription = fieldDescription;
1✔
2280
                            }
1✔
2281

2282
                            /**
2283
                             * {@inheritDoc}
2284
                             */
2285
                            public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
2286
                                return new Read(fieldDescription, assigner, typing);
1✔
2287
                            }
2288
                        }
2289
                    }
2290

2291
                    /**
2292
                     * A step for writing to a field.
2293
                     */
2294
                    @HashCodeAndEqualsPlugin.Enhance
2295
                    public static class Write extends ForField {
2296

2297
                        /**
2298
                         * The index of the parameter being accessed. If the targeted element is a non-static method, is increased by one.
2299
                         */
2300
                        private final int index;
2301

2302
                        /**
2303
                         * Creates a step for writing to a field.
2304
                         *
2305
                         * @param fieldDescription A description of the field to write to.
2306
                         * @param assigner         The assigner to use.
2307
                         * @param typing           The typing to use when assigning.
2308
                         * @param index            The index of the parameter being accessed. If the targeted element is a non-static method, is increased by one.
2309
                         */
2310
                        protected Write(FieldDescription fieldDescription, Assigner assigner, Assigner.Typing typing, int index) {
2311
                            super(fieldDescription, assigner, typing);
1✔
2312
                            this.index = index;
1✔
2313
                        }
1✔
2314

2315
                        /**
2316
                         * {@inheritDoc}
2317
                         */
2318
                        protected Resolution doResolve(ByteCodeElement.Member original, TypeList.Generic parameters, Map<Integer, Integer> offsets, StackManipulation stackManipulation) {
2319
                            int index = ((original.getModifiers() & Opcodes.ACC_STATIC) == 0)
1✔
2320
                                    && !(original instanceof MethodDescription
2321
                                    && ((MethodDescription) original).isConstructor()) ? this.index + 1 : this.index;
1✔
2322
                            if (index >= parameters.size()) {
1✔
2323
                                throw new IllegalStateException(original + " does not define an argument with index " + index);
×
2324
                            }
2325
                            StackManipulation assignment = assigner.assign(parameters.get(index), fieldDescription.getType(), typing);
1✔
2326
                            if (!assignment.isValid()) {
1✔
2327
                                throw new IllegalStateException("Cannot write " + parameters.get(index) + " to " + fieldDescription);
×
2328
                            }
2329
                            return new Simple(new StackManipulation.Compound(stackManipulation,
1✔
2330
                                    MethodVariableAccess.of(parameters.get(index)).loadFrom(offsets.get(index)),
1✔
2331
                                    assignment,
2332
                                    FieldAccess.forField(fieldDescription).write()), TypeDefinition.Sort.describe(void.class));
1✔
2333
                        }
2334

2335
                        /**
2336
                         * A factory for creating a step to write to a field.
2337
                         */
2338
                        @HashCodeAndEqualsPlugin.Enhance
2339
                        public static class Factory implements Step.Factory {
2340

2341
                            /**
2342
                             * A description of the field to write to.
2343
                             */
2344
                            private final FieldDescription fieldDescription;
2345

2346
                            /**
2347
                             * The index of the parameter being accessed. If the targeted element is a non-static method, is increased by one.
2348
                             */
2349
                            private final int index;
2350

2351
                            /**
2352
                             * Creates a factory for writing to a field.
2353
                             *
2354
                             * @param field The field to write to.
2355
                             * @param index The index of the parameter being accessed. If the targeted element is a non-static method, is increased by one.
2356
                             */
2357
                            public Factory(Field field, int index) {
2358
                                this(new FieldDescription.ForLoadedField(field), index);
1✔
2359
                            }
1✔
2360

2361
                            /**
2362
                             * Creates a factory for writing to a field.
2363
                             *
2364
                             * @param fieldDescription A description of the field to write to.
2365
                             * @param index            The index of the parameter being accessed. If the targeted element is a non-static method, is increased by one.
2366
                             */
2367
                            public Factory(FieldDescription fieldDescription, int index) {
1✔
2368
                                this.fieldDescription = fieldDescription;
1✔
2369
                                this.index = index;
1✔
2370
                            }
1✔
2371

2372
                            /**
2373
                             * {@inheritDoc}
2374
                             */
2375
                            public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
2376
                                return new Write(fieldDescription, assigner, typing, index);
1✔
2377
                            }
2378
                        }
2379
                    }
2380
                }
2381

2382
                /**
2383
                 * A step for invoking a method or constructor. If non-static, a method is invoked upon a the current stack argument of the chain.
2384
                 * Arguments are loaded from the intercepted byte code element with a possibility of substitution.
2385
                 */
2386
                @HashCodeAndEqualsPlugin.Enhance
2387
                class ForInvocation implements Step {
2388

2389
                    /**
2390
                     * The invoked method or constructor.
2391
                     */
2392
                    private final MethodDescription methodDescription;
2393

2394
                    /**
2395
                     * A mapping of substituted parameter indices. For targets that are non-static methods, the targeted index is increased by one.
2396
                     */
2397
                    private final Map<Integer, Integer> substitutions;
2398

2399
                    /**
2400
                     * The assigner to use.
2401
                     */
2402
                    private final Assigner assigner;
2403

2404
                    /**
2405
                     * The typing to use when assigning.
2406
                     */
2407
                    private final Assigner.Typing typing;
2408

2409
                    /**
2410
                     * Creates a new step of an invocation.
2411
                     *
2412
                     * @param methodDescription The invoked method or constructor.
2413
                     * @param substitutions     A mapping of substituted parameter indices. For targets that are non-static methods, the targeted index is increased by one.
2414
                     * @param assigner          The assigner to use.
2415
                     * @param typing            The typing to use when assigning.
2416
                     */
2417
                    protected ForInvocation(MethodDescription methodDescription, Map<Integer, Integer> substitutions, Assigner assigner, Assigner.Typing typing) {
1✔
2418
                        this.methodDescription = methodDescription;
1✔
2419
                        this.substitutions = substitutions;
1✔
2420
                        this.assigner = assigner;
1✔
2421
                        this.typing = typing;
1✔
2422
                    }
1✔
2423

2424
                    /**
2425
                     * {@inheritDoc}
2426
                     */
2427
                    public Resolution resolve(TypeDescription receiver,
2428
                                              ByteCodeElement.Member original,
2429
                                              TypeList.Generic parameters,
2430
                                              TypeDescription.Generic result,
2431
                                              JavaConstant.MethodHandle methodHandle,
2432
                                              StackManipulation stackManipulation,
2433
                                              TypeDescription.Generic current,
2434
                                              Map<Integer, Integer> offsets,
2435
                                              int freeOffset) {
2436
                        List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(3 + parameters.size() * 2);
1✔
2437
                        if (methodDescription.isStatic()) {
1✔
2438
                            stackManipulations.add(Removal.of(current));
1✔
2439
                        } else if (methodDescription.isConstructor()) {
1✔
2440
                            stackManipulations.add(Removal.of(current));
×
2441
                            stackManipulations.add(TypeCreation.of(methodDescription.getDeclaringType().asErasure()));
×
2442
                        } else {
2443
                            StackManipulation assignment = assigner.assign(current, methodDescription.getDeclaringType().asGenericType(), typing);
1✔
2444
                            if (!assignment.isValid()) {
1✔
2445
                                throw new IllegalStateException("Cannot assign " + current + " to " + methodDescription.getDeclaringType());
×
2446
                            }
2447
                            stackManipulations.add(assignment);
1✔
2448
                        }
2449
                        boolean shift = ((original.getModifiers() & Opcodes.ACC_STATIC) == 0) && !(original instanceof MethodDescription && ((MethodDescription) original).isConstructor());
1✔
2450
                        for (int index = 0; index < methodDescription.getParameters().size(); index++) {
1✔
2451
                            int substitution = substitutions.containsKey(index + (shift ? 1 : 0)) ? substitutions.get(index + (shift ? 1 : 0)) : index + (shift ? 1 : 0);
×
2452
                            if (substitution >= parameters.size()) {
×
2453
                                throw new IllegalStateException(original + " does not support an index " + substitution);
×
2454
                            }
2455
                            stackManipulations.add(MethodVariableAccess.of(parameters.get(substitution)).loadFrom(offsets.get(substitution)));
×
2456
                            StackManipulation assignment = assigner.assign(parameters.get(substitution), methodDescription.getParameters().get(index).getType(), typing);
×
2457
                            if (!assignment.isValid()) {
×
2458
                                throw new IllegalStateException("Cannot assign parameter with " + index + " of type " + parameters.get(substitution) + " to " + methodDescription);
×
2459
                            }
2460
                            stackManipulations.add(assignment);
×
2461
                        }
2462
                        stackManipulations.add(MethodInvocation.invoke(methodDescription));
1✔
2463
                        return new Simple(new StackManipulation.Compound(stackManipulations), methodDescription.getReturnType());
1✔
2464
                    }
2465

2466
                    /**
2467
                     * A factory to create a step for a method invocation.
2468
                     */
2469
                    @HashCodeAndEqualsPlugin.Enhance
2470
                    public static class Factory implements Step.Factory {
2471

2472
                        /**
2473
                         * The invoked method or constructor.
2474
                         */
2475
                        private final MethodDescription methodDescription;
2476

2477
                        /**
2478
                         * A mapping of substituted parameter indices. For targets that are non-static methods, the targeted index is increased by one.
2479
                         */
2480
                        private final Map<Integer, Integer> substitutions;
2481

2482
                        /**
2483
                         * Creates a factory for a method invocation without parameter substitutions.
2484
                         *
2485
                         * @param method The invoked method.
2486
                         */
2487
                        public Factory(Method method) {
2488
                            this(new MethodDescription.ForLoadedMethod(method));
1✔
2489
                        }
1✔
2490

2491
                        /**
2492
                         * Creates a factory for a method invocation without parameter substitutions.
2493
                         *
2494
                         * @param constructor The constructor.
2495
                         */
2496
                        public Factory(Constructor<?> constructor) {
2497
                            this(new MethodDescription.ForLoadedConstructor(constructor));
×
2498
                        }
×
2499

2500
                        /**
2501
                         * Creates a factory for a method invocation without parameter substitutions.
2502
                         *
2503
                         * @param methodDescription The invoked method or constructor.
2504
                         */
2505
                        public Factory(MethodDescription methodDescription) {
2506
                            this(methodDescription, Collections.<Integer, Integer>emptyMap());
1✔
2507
                        }
1✔
2508

2509
                        /**
2510
                         * Creates a factory for a method invocation.
2511
                         *
2512
                         * @param methodDescription The invoked method or constructor.
2513
                         * @param substitutions     A mapping of substituted parameter indices. For targets that are non-static methods,
2514
                         *                          the targeted index is increased by one.
2515
                         */
2516
                        public Factory(MethodDescription methodDescription, Map<Integer, Integer> substitutions) {
1✔
2517
                            this.methodDescription = methodDescription;
1✔
2518
                            this.substitutions = substitutions;
1✔
2519
                        }
1✔
2520

2521
                        /**
2522
                         * {@inheritDoc}
2523
                         */
2524
                        public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
2525
                            return new ForInvocation(methodDescription, substitutions, assigner, typing);
1✔
2526
                        }
2527
                    }
2528
                }
2529

2530
                /**
2531
                 * A step that invokes a delegation method based on annotations on the parameters of the targeted method.
2532
                 */
2533
                @HashCodeAndEqualsPlugin.Enhance
2534
                class ForDelegation implements Step {
2535

2536
                    /**
2537
                     * The type on top of the stack after the delegation is complete.
2538
                     */
2539
                    private final TypeDescription.Generic returned;
2540

2541
                    /**
2542
                     * The dispatcher to use.
2543
                     */
2544
                    private final Dispatcher.Resolved dispatcher;
2545

2546
                    /**
2547
                     * A list of offset mappings to execute prior to delegation.
2548
                     */
2549
                    private final List<OffsetMapping.Resolved> offsetMappings;
2550

2551
                    /**
2552
                     * @param returned       The type on top of the stack after the delegation is complete.
2553
                     * @param dispatcher     The dispatcher to use.
2554
                     * @param offsetMappings A list of offset mappings to execute prior to delegation.
2555
                     */
2556
                    protected ForDelegation(TypeDescription.Generic returned, Dispatcher.Resolved dispatcher, List<OffsetMapping.Resolved> offsetMappings) {
1✔
2557
                        this.returned = returned;
1✔
2558
                        this.dispatcher = dispatcher;
1✔
2559
                        this.offsetMappings = offsetMappings;
1✔
2560
                    }
1✔
2561

2562
                    /**
2563
                     * Returns a delegating step factory for the supplied method.
2564
                     *
2565
                     * @param method The method to delegate to.
2566
                     * @return An appropriate step factory.
2567
                     */
2568
                    public static Step.Factory to(Method method) {
2569
                        return to(new MethodDescription.ForLoadedMethod(method));
1✔
2570
                    }
2571

2572
                    /**
2573
                     * Returns a delegating step factory for the supplied constructor.
2574
                     *
2575
                     * @param constructor The constructor to delegate to.
2576
                     * @return An appropriate step factory.
2577
                     */
2578
                    public static Step.Factory to(Constructor<?> constructor) {
2579
                        return to(new MethodDescription.ForLoadedConstructor(constructor));
×
2580
                    }
2581

2582
                    /**
2583
                     * Returns a delegating step factory for the supplied method description..
2584
                     *
2585
                     * @param methodDescription A description of the method or constructor to delegate to.
2586
                     * @return An appropriate step factory.
2587
                     */
2588
                    public static Step.Factory to(MethodDescription.InDefinedShape methodDescription) {
2589
                        if (methodDescription.isTypeInitializer()) {
1✔
2590
                            throw new IllegalArgumentException("Cannot delegate to a type initializer: " + methodDescription);
×
2591
                        }
2592
                        return to(methodDescription, Dispatcher.ForRegularInvocation.Factory.INSTANCE, Collections.<OffsetMapping.Factory<?>>emptyList());
1✔
2593
                    }
2594

2595
                    /**
2596
                     * Creates an appropriate step factory for the given delegate method, dispatcher factory and user factories.
2597
                     *
2598
                     * @param delegate          A description of the method or constructor to delegate to.
2599
                     * @param dispatcherFactory The dispatcher factory to use.
2600
                     * @param userFactories     Factories for custom annotation bindings.
2601
                     * @return An appropriate step factory.
2602
                     */
2603
                    @SuppressWarnings("unchecked")
2604
                    private static Step.Factory to(MethodDescription.InDefinedShape delegate, Dispatcher.Factory dispatcherFactory, List<? extends OffsetMapping.Factory<?>> userFactories) {
2605
                        if (delegate.isTypeInitializer()) {
1✔
2606
                            throw new IllegalArgumentException("Cannot delegate to type initializer: " + delegate);
×
2607
                        }
2608
                        return new Factory(delegate, dispatcherFactory.make(delegate), CompoundList.of(Arrays.asList(
1✔
2609
                                OffsetMapping.ForArgument.Factory.INSTANCE,
2610
                                OffsetMapping.ForThisReference.Factory.INSTANCE,
2611
                                OffsetMapping.ForAllArguments.Factory.INSTANCE,
2612
                                OffsetMapping.ForSelfCallHandle.Factory.INSTANCE,
2613
                                OffsetMapping.ForField.Unresolved.Factory.INSTANCE,
2614
                                OffsetMapping.ForFieldHandle.Unresolved.GetterFactory.INSTANCE,
2615
                                OffsetMapping.ForFieldHandle.Unresolved.SetterFactory.INSTANCE,
2616
                                OffsetMapping.ForOrigin.Factory.INSTANCE,
2617
                                OffsetMapping.ForStubValue.Factory.INSTANCE,
2618
                                new OffsetMapping.ForStackManipulation.OfDefaultValue<Unused>(Unused.class),
2619
                                OffsetMapping.ForCurrent.Factory.INSTANCE), userFactories));
2620
                    }
2621

2622
                    /**
2623
                     * Returns a builder for creating a {@link ForDelegation} with custom configuration.
2624
                     *
2625
                     * @return A bulder for creating a custom delegator.
2626
                     */
2627
                    public static WithCustomMapping withCustomMapping() {
2628
                        return new WithCustomMapping(Dispatcher.ForRegularInvocation.Factory.INSTANCE, Collections.<Class<? extends Annotation>, OffsetMapping.Factory<?>>emptyMap());
1✔
2629
                    }
2630

2631
                    /**
2632
                     * {@inheritDoc}
2633
                     */
2634
                    public Resolution resolve(TypeDescription receiver,
2635
                                              ByteCodeElement.Member original,
2636
                                              TypeList.Generic parameters,
2637
                                              TypeDescription.Generic result,
2638
                                              JavaConstant.MethodHandle methodHandle,
2639
                                              StackManipulation stackManipulation,
2640
                                              TypeDescription.Generic current,
2641
                                              Map<Integer, Integer> offsets,
2642
                                              int freeOffset) {
2643
                        List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(offsetMappings.size() + 3);
1✔
2644
                        stackManipulations.add(current.represents(void.class)
1✔
2645
                                ? StackManipulation.Trivial.INSTANCE
2646
                                : MethodVariableAccess.of(current).storeAt(freeOffset));
1✔
2647
                        stackManipulations.add(dispatcher.initialize());
1✔
2648
                        for (OffsetMapping.Resolved offsetMapping : offsetMappings) {
1✔
2649
                            stackManipulations.add(offsetMapping.apply(receiver, original, parameters, result, current, methodHandle, offsets, freeOffset));
1✔
2650
                        }
1✔
2651
                        stackManipulations.add(dispatcher.apply(receiver, original, methodHandle));
1✔
2652
                        return new Simple(new StackManipulation.Compound(stackManipulations), returned);
1✔
2653
                    }
2654

2655
                    /**
2656
                     * A factory for creating a delegating step during a member substitution.
2657
                     */
2658
                    @HashCodeAndEqualsPlugin.Enhance
2659
                    protected static class Factory implements Step.Factory {
2660

2661
                        /**
2662
                         * A description of the method or constructor to delegate to.
2663
                         */
2664
                        private final MethodDescription.InDefinedShape delegate;
2665

2666
                        /**
2667
                         * The dispatcher to use for invoking the delegate.
2668
                         */
2669
                        private final Dispatcher dispatcher;
2670

2671
                        /**
2672
                         * The offset mappings to use.
2673
                         */
2674
                        private final List<OffsetMapping> offsetMappings;
2675

2676
                        /**
2677
                         * Creates a new factory for a delegating step.
2678
                         *
2679
                         * @param delegate   A description of the method or constructor to delegate to.
2680
                         * @param dispatcher The dispatcher to use for invoking the delegate.
2681
                         * @param factories  The dispatcher to use for invoking the delegate.
2682
                         */
2683
                        protected Factory(MethodDescription.InDefinedShape delegate, Dispatcher dispatcher, List<? extends OffsetMapping.Factory<?>> factories) {
1✔
2684
                            Map<TypeDescription, OffsetMapping.Factory<?>> offsetMappings = new HashMap<TypeDescription, OffsetMapping.Factory<?>>();
1✔
2685
                            for (OffsetMapping.Factory<?> factory : factories) {
1✔
2686
                                offsetMappings.put(net.bytebuddy.description.type.TypeDescription.ForLoadedType.of(factory.getAnnotationType()), factory);
1✔
2687
                            }
1✔
2688
                            this.offsetMappings = new ArrayList<OffsetMapping>(factories.size());
1✔
2689
                            if (delegate.isMethod() && !delegate.isStatic()) {
1✔
2690
                                OffsetMapping offsetMapping = null;
1✔
2691
                                for (AnnotationDescription annotationDescription : delegate.getDeclaredAnnotations()) {
1✔
2692
                                    OffsetMapping.Factory<?> factory = offsetMappings.get(annotationDescription.getAnnotationType());
×
2693
                                    if (factory != null) {
×
2694
                                        @SuppressWarnings("unchecked") OffsetMapping current = factory.make(delegate, (AnnotationDescription.Loadable) annotationDescription.prepare(factory.getAnnotationType()));
×
2695
                                        if (offsetMapping == null) {
×
2696
                                            offsetMapping = current;
×
2697
                                        } else {
2698
                                            throw new IllegalStateException(delegate + " is bound to both " + current + " and " + offsetMapping);
×
2699
                                        }
2700
                                    }
2701
                                }
×
2702
                                this.offsetMappings.add(offsetMapping == null
1✔
2703
                                        ? new OffsetMapping.ForThisReference(delegate.getDeclaringType().asGenericType(), null, Source.SUBSTITUTED_ELEMENT, false)
1✔
2704
                                        : offsetMapping);
2705
                            }
2706
                            for (int index = 0; index < delegate.getParameters().size(); index++) {
1✔
2707
                                ParameterDescription.InDefinedShape parameterDescription = delegate.getParameters().get(index);
1✔
2708
                                OffsetMapping offsetMapping = null;
1✔
2709
                                for (AnnotationDescription annotationDescription : parameterDescription.getDeclaredAnnotations()) {
1✔
2710
                                    OffsetMapping.Factory<?> factory = offsetMappings.get(annotationDescription.getAnnotationType());
1✔
2711
                                    if (factory != null) {
1✔
2712
                                        @SuppressWarnings("unchecked") OffsetMapping current = factory.make(parameterDescription, (AnnotationDescription.Loadable) annotationDescription.prepare(factory.getAnnotationType()));
1✔
2713
                                        if (offsetMapping == null) {
1✔
2714
                                            offsetMapping = current;
1✔
2715
                                        } else {
2716
                                            throw new IllegalStateException(parameterDescription + " is bound to both " + current + " and " + offsetMapping);
×
2717
                                        }
2718
                                    }
2719
                                }
1✔
2720
                                this.offsetMappings.add(offsetMapping == null
1✔
2721
                                        ? new OffsetMapping.ForArgument(parameterDescription.getType(), index, null, Source.SUBSTITUTED_ELEMENT, false)
1✔
2722
                                        : offsetMapping);
2723
                            }
2724
                            this.delegate = delegate;
1✔
2725
                            this.dispatcher = dispatcher;
1✔
2726
                        }
1✔
2727

2728
                        /**
2729
                         * {@inheritDoc}
2730
                         */
2731
                        public Step make(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
2732
                            List<OffsetMapping.Resolved> targets = new ArrayList<OffsetMapping.Resolved>(offsetMappings.size());
1✔
2733
                            for (OffsetMapping offsetMapping : offsetMappings) {
1✔
2734
                                targets.add(offsetMapping.resolve(assigner, typing, instrumentedType, instrumentedMethod));
1✔
2735
                            }
1✔
2736
                            return new ForDelegation(delegate.getReturnType(), dispatcher.resolve(instrumentedType, instrumentedMethod), targets);
1✔
2737
                        }
2738
                    }
2739

2740
                    /**
2741
                     * An offset mapping for binding a parameter or dispatch target for the method or constructor that is delegated to.
2742
                     */
2743
                    public interface OffsetMapping {
2744

2745
                        /**
2746
                         * Resolves an offset mapping for a given instrumented method.
2747
                         *
2748
                         * @param assigner           The assigner to use.
2749
                         * @param typing             The typing to use if no explicit typing is specified.
2750
                         * @param instrumentedType   The instrumented type.
2751
                         * @param instrumentedMethod The instrumented method.
2752
                         * @return A resolved version of this offset mapping.
2753
                         */
2754
                        OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod);
2755

2756
                        /**
2757
                         * An offset mapping that was resolved for a given instrumented type and method.
2758
                         */
2759
                        interface Resolved {
2760

2761
                            /**
2762
                             * Applies this offset mapping.
2763
                             *
2764
                             * @param receiver     The target type of the invoked delegate.
2765
                             * @param original     The substituted element.
2766
                             * @param parameters   The parameters that are supplied to the substituted expression.
2767
                             * @param result       The resulting type of the substituted expression.
2768
                             * @param current      The type of the value that was produced by the previous step in the substitution chain.
2769
                             * @param methodHandle A method handle that represents the substituted element.
2770
                             * @param offsets      The offsets of the supplied parameters.
2771
                             * @param offset       The offset of the value that was produced by the previous step.
2772
                             * @return An appropriate stack manipulation.
2773
                             */
2774
                            StackManipulation apply(TypeDescription receiver,
2775
                                                    ByteCodeElement.Member original,
2776
                                                    TypeList.Generic parameters,
2777
                                                    TypeDescription.Generic result,
2778
                                                    TypeDescription.Generic current,
2779
                                                    JavaConstant.MethodHandle methodHandle,
2780
                                                    Map<Integer, Integer> offsets,
2781
                                                    int offset);
2782

2783
                            /**
2784
                             * An offset mapping that loads a stack manipulation.
2785
                             */
2786
                            @HashCodeAndEqualsPlugin.Enhance
2787
                            class ForStackManipulation implements OffsetMapping.Resolved {
2788

2789
                                /**
2790
                                 * The stack manipulation to load.
2791
                                 */
2792
                                private final StackManipulation stackManipulation;
2793

2794
                                /**
2795
                                 * Creates a resolved offset mapping for a stack manipulation.
2796
                                 *
2797
                                 * @param stackManipulation The stack manipulation to load.
2798
                                 */
2799
                                public ForStackManipulation(StackManipulation stackManipulation) {
1✔
2800
                                    this.stackManipulation = stackManipulation;
1✔
2801
                                }
1✔
2802

2803
                                /**
2804
                                 * {@inheritDoc}
2805
                                 */
2806
                                public StackManipulation apply(TypeDescription receiver,
2807
                                                               ByteCodeElement.Member original,
2808
                                                               TypeList.Generic parameters,
2809
                                                               TypeDescription.Generic result,
2810
                                                               TypeDescription.Generic current,
2811
                                                               JavaConstant.MethodHandle methodHandle,
2812
                                                               Map<Integer, Integer> offsets,
2813
                                                               int offset) {
2814
                                    return stackManipulation;
1✔
2815
                                }
2816
                            }
2817
                        }
2818

2819
                        /**
2820
                         * A factory for creating an offset mapping based on an annotation on a parameter, method or constructor.
2821
                         *
2822
                         * @param <T> The type of the annotation.
2823
                         */
2824
                        interface Factory<T extends Annotation> {
2825

2826
                            /**
2827
                             * Returns the type of the annotation for this factory.
2828
                             *
2829
                             * @return The type of the annotation for this factory.
2830
                             */
2831
                            Class<T> getAnnotationType();
2832

2833
                            /**
2834
                             * Creates an offset mapping for an annotation that was found on a non-static method.
2835
                             *
2836
                             * @param target     The method that is the delegated to.
2837
                             * @param annotation The annotation that was found on the method.
2838
                             * @return An appropriate offset mapping.
2839
                             */
2840
                            OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<T> annotation);
2841

2842
                            /**
2843
                             * Creates an offset mapping for a parameter of the method or constructor that is the delegation target.
2844
                             *
2845
                             * @param target     The parameter that is bound to an expression.
2846
                             * @param annotation The annotation that was found on the parameter.
2847
                             * @return An appropriate offset mapping.
2848
                             */
2849
                            OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<T> annotation);
2850

2851
                            /**
2852
                             * An abstract base implementation of a factory for an offset mapping.
2853
                             *
2854
                             * @param <S> The type of the represented annotation.
2855
                             */
2856
                            abstract class AbstractBase<S extends Annotation> implements OffsetMapping.Factory<S> {
1✔
2857

2858
                                /**
2859
                                 * {@inheritDoc}
2860
                                 */
2861
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<S> annotation) {
2862
                                    return make(target.getDeclaringType().asGenericType(), annotation);
×
2863
                                }
2864

2865
                                /**
2866
                                 * {@inheritDoc}
2867
                                 */
2868
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<S> annotation) {
2869
                                    return make(target.getType(), annotation);
1✔
2870
                                }
2871

2872
                                /**
2873
                                 * Returns an offset mapping for the bound method target or parameter.
2874
                                 *
2875
                                 * @param target     The declaring type of a non-static method or a parameter type.
2876
                                 * @param annotation The annotation that was found on the method or parameter.
2877
                                 * @return An appropriate offset mapping.
2878
                                 */
2879
                                protected abstract OffsetMapping make(TypeDescription.Generic target, AnnotationDescription.Loadable<S> annotation);
2880
                            }
2881

2882
                            /**
2883
                             * A factory for an offset mapping that does not support binding a method target.
2884
                             *
2885
                             * @param <S> The type of the represented annotation.
2886
                             */
2887
                            abstract class WithParameterSupportOnly<S extends Annotation> implements OffsetMapping.Factory<S> {
×
2888

2889
                                /**
2890
                                 * {@inheritDoc}
2891
                                 */
2892
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<S> annotation) {
2893
                                    throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
2894
                                }
2895
                            }
2896

2897
                            /**
2898
                             * A simple factory for an offset mapping.
2899
                             *
2900
                             * @param <S> The type of the represented annotation.
2901
                             */
2902
                            @HashCodeAndEqualsPlugin.Enhance
2903
                            class Simple<S extends Annotation> extends OffsetMapping.Factory.AbstractBase<S> {
2904

2905
                                /**
2906
                                 * The type of the bound annotation.
2907
                                 */
2908
                                private final Class<S> annotationType;
2909

2910
                                /**
2911
                                 * The offset mapping to return.
2912
                                 */
2913
                                private final OffsetMapping offsetMapping;
2914

2915
                                /**
2916
                                 * Creates a simple factory for an offset mapping.
2917
                                 *
2918
                                 * @param annotationType The type of the bound annotation.
2919
                                 * @param offsetMapping  The offset mapping to return.
2920
                                 */
2921
                                public Simple(Class<S> annotationType, OffsetMapping offsetMapping) {
×
2922
                                    this.annotationType = annotationType;
×
2923
                                    this.offsetMapping = offsetMapping;
×
2924
                                }
×
2925

2926
                                /**
2927
                                 * {@inheritDoc}
2928
                                 */
2929
                                public Class<S> getAnnotationType() {
2930
                                    return annotationType;
×
2931
                                }
2932

2933
                                @Override
2934
                                protected OffsetMapping make(TypeDescription.Generic target, AnnotationDescription.Loadable<S> annotation) {
2935
                                    return offsetMapping;
×
2936
                                }
2937
                            }
2938
                        }
2939

2940
                        /**
2941
                         * An offset mapping that resolves a given stack manipulation.
2942
                         */
2943
                        @HashCodeAndEqualsPlugin.Enhance
2944
                        class ForStackManipulation implements OffsetMapping {
2945

2946
                            /**
2947
                             * The stack manipulation to apply.
2948
                             */
2949
                            private final StackManipulation stackManipulation;
2950

2951
                            /**
2952
                             * The type of the value that is produced by the stack manipulation.
2953
                             */
2954
                            private final TypeDescription.Generic typeDescription;
2955

2956
                            /**
2957
                             * The type of the parameter or method target that is bound by this mapping.
2958
                             */
2959
                            private final TypeDescription.Generic targetType;
2960

2961
                            /**
2962
                             * Creates a new offset mapping for a stack manipulation.
2963
                             *
2964
                             * @param stackManipulation The stack manipulation to apply.
2965
                             * @param typeDescription   The type of the value that is produced by the stack manipulation.
2966
                             * @param targetType        The type of the parameter or method target that is bound by this mapping.
2967
                             */
2968
                            public ForStackManipulation(StackManipulation stackManipulation, TypeDescription.Generic typeDescription, TypeDescription.Generic targetType) {
1✔
2969
                                this.targetType = targetType;
1✔
2970
                                this.stackManipulation = stackManipulation;
1✔
2971
                                this.typeDescription = typeDescription;
1✔
2972
                            }
1✔
2973

2974
                            /**
2975
                             * Resolves an offset mapping that binds the provided annotation type to a given constant value.
2976
                             *
2977
                             * @param annotationType The annotation type to bind.
2978
                             * @param value          The constant value being bound or {@code null}.
2979
                             * @param <S>            The type of the annotation.
2980
                             * @return An appropriate factory for an offset mapping.
2981
                             */
2982
                            public static <S extends Annotation> OffsetMapping.Factory<S> of(Class<S> annotationType, @MaybeNull Object value) {
2983
                                return value == null
×
2984
                                        ? new OffsetMapping.ForStackManipulation.OfDefaultValue<S>(annotationType)
2985
                                        : new OffsetMapping.ForStackManipulation.Factory<S>(annotationType, ConstantValue.Simple.wrap(value));
×
2986
                            }
2987

2988
                            /**
2989
                             * {@inheritDoc}
2990
                             */
2991
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
2992
                                return new ForStackManipulation.Resolved(assigner, typing, stackManipulation, typeDescription, targetType);
1✔
2993
                            }
2994

2995
                            /**
2996
                             * A resolved offset mapping for a stack manipulation.
2997
                             */
2998
                            @HashCodeAndEqualsPlugin.Enhance
2999
                            protected static class Resolved implements OffsetMapping.Resolved {
3000

3001
                                /**
3002
                                 * The assigner to use.
3003
                                 */
3004
                                private final Assigner assigner;
3005

3006
                                /**
3007
                                 * The typing to apply.
3008
                                 */
3009
                                private final Assigner.Typing typing;
3010

3011
                                /**
3012
                                 * The stack manipulation to apply.
3013
                                 */
3014
                                private final StackManipulation stackManipulation;
3015

3016
                                /**
3017
                                 * The type of the value that is produced by the stack manipulation.
3018
                                 */
3019
                                private final TypeDescription.Generic typeDescription;
3020

3021
                                /**
3022
                                 * The type of the parameter or method target that is bound by this mapping.
3023
                                 */
3024
                                private final TypeDescription.Generic targetType;
3025

3026
                                /**
3027
                                 * Creates a resolved offset mapping for a given stack manipulation.
3028
                                 *
3029
                                 * @param assigner          The assigner to use.
3030
                                 * @param typing            The typing to apply.
3031
                                 * @param stackManipulation The stack manipulation to apply.
3032
                                 * @param typeDescription   The type of the value that is produced by the stack manipulation.
3033
                                 * @param targetType        The type of the parameter or method target that is bound by this mapping.
3034
                                 */
3035
                                protected Resolved(Assigner assigner,
3036
                                                   Assigner.Typing typing,
3037
                                                   StackManipulation stackManipulation,
3038
                                                   TypeDescription.Generic typeDescription,
3039
                                                   TypeDescription.Generic targetType) {
1✔
3040
                                    this.assigner = assigner;
1✔
3041
                                    this.typing = typing;
1✔
3042
                                    this.stackManipulation = stackManipulation;
1✔
3043
                                    this.typeDescription = typeDescription;
1✔
3044
                                    this.targetType = targetType;
1✔
3045
                                }
1✔
3046

3047
                                /**
3048
                                 * {@inheritDoc}
3049
                                 */
3050
                                public StackManipulation apply(TypeDescription receiver,
3051
                                                               ByteCodeElement.Member original,
3052
                                                               TypeList.Generic parameters,
3053
                                                               TypeDescription.Generic result,
3054
                                                               TypeDescription.Generic current,
3055
                                                               JavaConstant.MethodHandle methodHandle,
3056
                                                               Map<Integer, Integer> offsets,
3057
                                                               int offset) {
3058
                                    StackManipulation assignment = assigner.assign(typeDescription, targetType, typing);
1✔
3059
                                    if (!assignment.isValid()) {
1✔
3060
                                        throw new IllegalStateException("Cannot assign " + typeDescription + " to " + targetType);
×
3061
                                    }
3062
                                    return new StackManipulation.Compound(stackManipulation, assignment);
1✔
3063
                                }
3064
                            }
3065

3066
                            /**
3067
                             * A factory that binds the default value of the annotated parameter, i.e. {@code null} for reference types
3068
                             * or the specific version of {@code 0} for primitive types.
3069
                             *
3070
                             * @param <T> The type of the annotation.
3071
                             */
3072
                            @HashCodeAndEqualsPlugin.Enhance
3073
                            public static class OfDefaultValue<T extends Annotation> implements OffsetMapping.Factory<T> {
3074

3075
                                /**
3076
                                 * The annotation type.
3077
                                 */
3078
                                private final Class<T> annotationType;
3079

3080
                                /**
3081
                                 * Creates a new factory for binding a default value.
3082
                                 *
3083
                                 * @param annotationType The annotation type.
3084
                                 */
3085
                                public OfDefaultValue(Class<T> annotationType) {
1✔
3086
                                    this.annotationType = annotationType;
1✔
3087
                                }
1✔
3088

3089
                                /**
3090
                                 * {@inheritDoc}
3091
                                 */
3092
                                public Class<T> getAnnotationType() {
3093
                                    return annotationType;
1✔
3094
                                }
3095

3096
                                /**
3097
                                 * {@inheritDoc}
3098
                                 */
3099
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<T> annotation) {
3100
                                    throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
3101
                                }
3102

3103
                                /**
3104
                                 * {@inheritDoc}
3105
                                 */
3106
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<T> annotation) {
3107
                                    return new ForStackManipulation(DefaultValue.of(target.getType()), target.getType(), target.getType());
1✔
3108
                                }
3109
                            }
3110

3111
                            /**
3112
                             * A factory that binds a given annotation property to the parameter.
3113
                             *
3114
                             * @param <T> The type of the annotation.
3115
                             */
3116
                            @HashCodeAndEqualsPlugin.Enhance
3117
                            public static class OfAnnotationProperty<T extends Annotation> extends OffsetMapping.Factory.WithParameterSupportOnly<T> {
3118

3119
                                /**
3120
                                 * The annotation type.
3121
                                 */
3122
                                private final Class<T> annotationType;
3123

3124
                                /**
3125
                                 * The annotation property to resolve.
3126
                                 */
3127
                                private final MethodDescription.InDefinedShape property;
3128

3129
                                /**
3130
                                 * Creates a factory for assigning an annotation property to the annotated parameter.
3131
                                 *
3132
                                 * @param annotationType The annotation type.
3133
                                 * @param property       The annotation property to resolve.
3134
                                 */
3135
                                protected OfAnnotationProperty(Class<T> annotationType, MethodDescription.InDefinedShape property) {
×
3136
                                    this.annotationType = annotationType;
×
3137
                                    this.property = property;
×
3138
                                }
×
3139

3140
                                /**
3141
                                 * Resolves an offset mapping factory where the provided property is assigned to any parameter that
3142
                                 * is annotated with the given annotation.
3143
                                 *
3144
                                 * @param annotationType The annotation type.
3145
                                 * @param property       The name of the property on the
3146
                                 * @param <S>            The type of the annotation from which the property is read.
3147
                                 * @return An appropriate factory for an offset mapping.
3148
                                 */
3149
                                public static <S extends Annotation> OffsetMapping.Factory<S> of(Class<S> annotationType, String property) {
3150
                                    if (!annotationType.isAnnotation()) {
×
3151
                                        throw new IllegalArgumentException("Not an annotation type: " + annotationType);
×
3152
                                    }
3153
                                    try {
3154
                                        return new ForStackManipulation.OfAnnotationProperty<S>(annotationType, new MethodDescription.ForLoadedMethod(annotationType.getMethod(property)));
×
3155
                                    } catch (NoSuchMethodException exception) {
×
3156
                                        throw new IllegalArgumentException("Cannot find a property " + property + " on " + annotationType, exception);
×
3157
                                    }
3158
                                }
3159

3160
                                /**
3161
                                 * {@inheritDoc}
3162
                                 */
3163
                                public Class<T> getAnnotationType() {
3164
                                    return annotationType;
×
3165
                                }
3166

3167
                                /**
3168
                                 * {@inheritDoc}
3169
                                 */
3170
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<T> annotation) {
3171
                                    ConstantValue value = ConstantValue.Simple.wrapOrNull(annotation.getValue(property).resolve());
×
3172
                                    if (value == null) {
×
3173
                                        throw new IllegalStateException("Not a constant value property: " + property);
×
3174
                                    }
3175
                                    return new ForStackManipulation(value.toStackManipulation(), value.getTypeDescription().asGenericType(), target.getType());
×
3176
                                }
3177
                            }
3178

3179
                            /**
3180
                             * Assigns a value to the annotated parameter that is deserialized from a given input.
3181
                             *
3182
                             * @param <T> The type of the annotation.
3183
                             */
3184
                            @HashCodeAndEqualsPlugin.Enhance
3185
                            public static class OfSerializedConstant<T extends Annotation> extends OffsetMapping.Factory.AbstractBase<T> {
3186

3187
                                /**
3188
                                 * The annotation type.
3189
                                 */
3190
                                private final Class<T> annotationType;
3191

3192
                                /**
3193
                                 * A stack manipulation that represents the deserialization.
3194
                                 */
3195
                                private final StackManipulation deserialization;
3196

3197
                                /**
3198
                                 * A description of the type that is returned as a result of the deserialization.
3199
                                 */
3200
                                private final TypeDescription.Generic typeDescription;
3201

3202
                                /**
3203
                                 * Creates a factory that creates an offset mapping for a value that is deserialized.
3204
                                 *
3205
                                 * @param annotationType  The annotation type.
3206
                                 * @param deserialization A stack manipulation that represents the deserialization.
3207
                                 * @param typeDescription A description of the type that is returned as a result of the deserialization.
3208
                                 */
3209
                                protected OfSerializedConstant(Class<T> annotationType, StackManipulation deserialization, TypeDescription.Generic typeDescription) {
1✔
3210
                                    this.annotationType = annotationType;
1✔
3211
                                    this.deserialization = deserialization;
1✔
3212
                                    this.typeDescription = typeDescription;
1✔
3213
                                }
1✔
3214

3215
                                /**
3216
                                 * Creates a factory for an offset mapping that deserializes a given value that is then assigned to the annotated parameter or used as a method target.
3217
                                 *
3218
                                 * @param type       The annotation type.
3219
                                 * @param value      The serialized value.
3220
                                 * @param targetType The type of the value that is deserialized.
3221
                                 * @param <S>        The type of the annotation.
3222
                                 * @param <U>        The type of the serialized value.
3223
                                 * @return An appropriate factory for an offset mapping.
3224
                                 */
3225
                                public static <S extends Annotation, U extends Serializable> OffsetMapping.Factory<S> of(Class<S> type, U value, Class<? super U> targetType) {
3226
                                    if (!targetType.isInstance(value)) {
1✔
3227
                                        throw new IllegalArgumentException(value + " is no instance of " + targetType);
×
3228
                                    }
3229
                                    return new ForStackManipulation.OfSerializedConstant<S>(type, SerializedConstant.of(value), net.bytebuddy.description.type.TypeDescription.ForLoadedType.of(targetType).asGenericType());
1✔
3230
                                }
3231

3232
                                /**
3233
                                 * {@inheritDoc}
3234
                                 */
3235
                                public Class<T> getAnnotationType() {
3236
                                    return annotationType;
1✔
3237
                                }
3238

3239
                                @Override
3240
                                protected OffsetMapping make(TypeDescription.Generic target, AnnotationDescription.Loadable<T> annotation) {
3241
                                    return new ForStackManipulation(deserialization, typeDescription, target);
1✔
3242
                                }
3243
                            }
3244

3245
                            /**
3246
                             * A factory that invokes a method dynamically and assignes the result to the annotated parameter.
3247
                             *
3248
                             * @param <T> The type of the annotation.
3249
                             */
3250
                            @HashCodeAndEqualsPlugin.Enhance
3251
                            public static class OfDynamicInvocation<T extends Annotation> extends OffsetMapping.Factory.AbstractBase<T> {
3252

3253
                                /**
3254
                                 * The annotation type.
3255
                                 */
3256
                                private final Class<T> annotationType;
3257

3258
                                /**
3259
                                 * The bootstrap method to use.
3260
                                 */
3261
                                private final MethodDescription.InDefinedShape bootstrapMethod;
3262

3263
                                /**
3264
                                 * The constants to provide to the bootstrap method.
3265
                                 */
3266
                                private final List<? extends JavaConstant> arguments;
3267

3268
                                /**
3269
                                 * Creates a factory for an offset mapping that assigns the result of a dynamic method invocation.
3270
                                 *
3271
                                 * @param annotationType  The annotation type.
3272
                                 * @param bootstrapMethod The bootstrap method to use.
3273
                                 * @param arguments       The constants to provide to the bootstrap method.
3274
                                 */
3275
                                public OfDynamicInvocation(Class<T> annotationType, MethodDescription.InDefinedShape bootstrapMethod, List<? extends JavaConstant> arguments) {
×
3276
                                    this.annotationType = annotationType;
×
3277
                                    this.bootstrapMethod = bootstrapMethod;
×
3278
                                    this.arguments = arguments;
×
3279
                                }
×
3280

3281
                                /**
3282
                                 * {@inheritDoc}
3283
                                 */
3284
                                public Class<T> getAnnotationType() {
3285
                                    return annotationType;
×
3286
                                }
3287

3288
                                @Override
3289
                                protected OffsetMapping make(TypeDescription.Generic target, AnnotationDescription.Loadable<T> annotation) {
3290
                                    if (!target.isInterface()) {
×
3291
                                        throw new IllegalArgumentException(target + " is not an interface");
×
3292
                                    } else if (!target.getInterfaces().isEmpty()) {
×
3293
                                        throw new IllegalArgumentException(target + " must not extend other interfaces");
×
3294
                                    } else if (!target.isPublic()) {
×
3295
                                        throw new IllegalArgumentException(target + " is mot public");
×
3296
                                    }
3297
                                    MethodList<?> methodCandidates = target.getDeclaredMethods().filter(isAbstract());
×
3298
                                    if (methodCandidates.size() != 1) {
×
3299
                                        throw new IllegalArgumentException(target + " must declare exactly one abstract method");
×
3300
                                    }
3301
                                    return new OffsetMapping.ForStackManipulation(MethodInvocation.invoke(bootstrapMethod).dynamic(methodCandidates.getOnly().getInternalName(),
×
3302
                                            target.asErasure(),
×
3303
                                            Collections.<TypeDescription>emptyList(),
×
3304
                                            arguments), target, target);
3305
                                }
3306
                            }
3307

3308
                            /**
3309
                             * A factory to produce an offset mapping based upon a stack manipulation..
3310
                             *
3311
                             * @param <T> The type of the annotation.
3312
                             */
3313
                            @HashCodeAndEqualsPlugin.Enhance
3314
                            public static class Factory<T extends Annotation> extends OffsetMapping.Factory.AbstractBase<T> {
3315

3316
                                /**
3317
                                 * The annotation type.
3318
                                 */
3319
                                private final Class<T> annotationType;
3320

3321
                                /**
3322
                                 * The stack manipulation that produces the assigned value.
3323
                                 */
3324
                                private final StackManipulation stackManipulation;
3325

3326
                                /**
3327
                                 * The type of the value that is produced by the stack manipulation.
3328
                                 */
3329
                                private final TypeDescription.Generic typeDescription;
3330

3331
                                /**
3332
                                 * Creates a factory for a given constant value.
3333
                                 *
3334
                                 * @param annotationType The value to assign to the parameter.
3335
                                 * @param value          The value that is bound.
3336
                                 */
3337
                                public Factory(Class<T> annotationType, ConstantValue value) {
3338
                                    this(annotationType, value.toStackManipulation(), value.getTypeDescription().asGenericType());
×
3339
                                }
×
3340

3341
                                /**
3342
                                 * Creates a factory for a given stack manipulation.
3343
                                 *
3344
                                 * @param annotationType    The value to assign to the parameter.
3345
                                 * @param stackManipulation The stack manipulation that produces the assigned value.
3346
                                 * @param typeDescription   The type of the value that is produced by the stack manipulation.
3347
                                 */
3348
                                public Factory(Class<T> annotationType, StackManipulation stackManipulation, TypeDescription.Generic typeDescription) {
×
3349
                                    this.annotationType = annotationType;
×
3350
                                    this.stackManipulation = stackManipulation;
×
3351
                                    this.typeDescription = typeDescription;
×
3352
                                }
×
3353

3354
                                /**
3355
                                 * {@inheritDoc}
3356
                                 */
3357
                                public Class<T> getAnnotationType() {
3358
                                    return annotationType;
×
3359
                                }
3360

3361
                                @Override
3362
                                protected OffsetMapping make(TypeDescription.Generic target, AnnotationDescription.Loadable<T> annotation) {
3363
                                    return new ForStackManipulation(stackManipulation, typeDescription, target);
×
3364
                                }
3365
                            }
3366
                        }
3367

3368
                        /**
3369
                         * An offset mapping that assigns an argument of either the instrumented
3370
                         * method or the substituted expression.
3371
                         */
3372
                        @HashCodeAndEqualsPlugin.Enhance
3373
                        class ForArgument implements OffsetMapping {
3374

3375
                            /**
3376
                             * A description of the targeted type.
3377
                             */
3378
                            private final TypeDescription.Generic targetType;
3379

3380
                            /**
3381
                             * The index of the parameter.
3382
                             */
3383
                            private final int index;
3384

3385
                            /**
3386
                             * The typing to use or {@code null} if the global typing setting should be applied.
3387
                             */
3388
                            @MaybeNull
3389
                            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
3390
                            private final Assigner.Typing typing;
3391

3392
                            /**
3393
                             * The source providing the argument.
3394
                             */
3395
                            private final Source source;
3396

3397
                            /**
3398
                             * {@code true} if {@code null} or a primitive {@code 0} should be assigned to the parameter
3399
                             * if the provided index is not available.
3400
                             */
3401
                            private final boolean optional;
3402

3403
                            /**
3404
                             * Creates a new offset mapping for an argument to either the substituted expression or the instrumented method.
3405
                             *
3406
                             * @param targetType A description of the targeted type.
3407
                             * @param index      The index of the parameter.
3408
                             * @param typing     The typing to use or {@code null} if the global typing setting should be applied.
3409
                             * @param source     The source providing the argument.
3410
                             * @param optional   {@code true} if {@code null} or a primitive {@code 0} should be assigned to the parameter
3411
                             *                   if the provided index is not available.
3412
                             */
3413
                            public ForArgument(TypeDescription.Generic targetType, int index, @MaybeNull Assigner.Typing typing, Source source, boolean optional) {
1✔
3414
                                this.targetType = targetType;
1✔
3415
                                this.index = index;
1✔
3416
                                this.typing = typing;
1✔
3417
                                this.source = source;
1✔
3418
                                this.optional = optional;
1✔
3419
                            }
1✔
3420

3421
                            /**
3422
                             * {@inheritDoc}
3423
                             */
3424
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
3425
                                return new ForArgument.Resolved(targetType, index, this.typing == null ? typing : this.typing, source, optional, assigner, instrumentedMethod);
1✔
3426
                            }
3427

3428
                            /**
3429
                             * A factory for creating an offset mapping for a parameter value of either the instrumented
3430
                             * method or the substituted element.
3431
                             */
3432
                            protected enum Factory implements OffsetMapping.Factory<Argument> {
1✔
3433

3434
                                /**
3435
                                 * The singleton instance.
3436
                                 */
3437
                                INSTANCE;
1✔
3438

3439
                                /**
3440
                                 * The {@link Argument#value()} property.
3441
                                 */
3442
                                private static final MethodDescription.InDefinedShape ARGUMENT_VALUE;
3443

3444
                                /**
3445
                                 * The {@link Argument#typing()} property.
3446
                                 */
3447
                                private static final MethodDescription.InDefinedShape ARGUMENT_TYPING;
3448

3449
                                /**
3450
                                 * The {@link Argument#source()} property.
3451
                                 */
3452
                                private static final MethodDescription.InDefinedShape ARGUMENT_SOURCE;
3453

3454
                                /**
3455
                                 * The {@link Argument#optional()} property.
3456
                                 */
3457
                                private static final MethodDescription.InDefinedShape ARGUMENT_OPTIONAL;
3458

3459
                                /*
3460
                                 * Resolves all annotation properties.
3461
                                 */
3462
                                static {
3463
                                    MethodList<MethodDescription.InDefinedShape> methods = net.bytebuddy.description.type.TypeDescription.ForLoadedType.of(Argument.class).getDeclaredMethods();
1✔
3464
                                    ARGUMENT_VALUE = methods.filter(named("value")).getOnly();
1✔
3465
                                    ARGUMENT_TYPING = methods.filter(named("typing")).getOnly();
1✔
3466
                                    ARGUMENT_SOURCE = methods.filter(named("source")).getOnly();
1✔
3467
                                    ARGUMENT_OPTIONAL = methods.filter(named("optional")).getOnly();
1✔
3468
                                }
1✔
3469

3470
                                /**
3471
                                 * {@inheritDoc}
3472
                                 */
3473
                                public Class<Argument> getAnnotationType() {
3474
                                    return Argument.class;
1✔
3475
                                }
3476

3477
                                /**
3478
                                 * {@inheritDoc}
3479
                                 */
3480
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<Argument> annotation) {
3481
                                    return new ForArgument(target.getDeclaringType().asGenericType(),
×
3482
                                            annotation.getValue(ARGUMENT_VALUE).resolve(Integer.class),
×
3483
                                            annotation.getValue(ARGUMENT_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class),
×
3484
                                            annotation.getValue(ARGUMENT_SOURCE).resolve(EnumerationDescription.class).load(Source.class),
×
3485
                                            annotation.getValue(ARGUMENT_OPTIONAL).resolve(Boolean.class));
×
3486
                                }
3487

3488
                                /**
3489
                                 * {@inheritDoc}
3490
                                 */
3491
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<Argument> annotation) {
3492
                                    int index = annotation.getValue(ARGUMENT_VALUE).resolve(Integer.class);
1✔
3493
                                    if (index < 0) {
1✔
3494
                                        throw new IllegalStateException("Cannot assign negative parameter index " + index + " for " + target);
×
3495
                                    }
3496
                                    return new ForArgument(target.getType(),
1✔
3497
                                            index,
3498
                                            annotation.getValue(ARGUMENT_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class),
1✔
3499
                                            annotation.getValue(ARGUMENT_SOURCE).resolve(EnumerationDescription.class).load(Source.class),
1✔
3500
                                            annotation.getValue(ARGUMENT_OPTIONAL).resolve(Boolean.class));
1✔
3501
                                }
3502
                            }
3503

3504
                            /**
3505
                             * A resolved offset mapping to the parameter of either the instrumented method or
3506
                             * the substituted element.
3507
                             */
3508
                            @HashCodeAndEqualsPlugin.Enhance
3509
                            protected static class Resolved implements OffsetMapping.Resolved {
3510

3511
                                /**
3512
                                 * The targeted type.
3513
                                 */
3514
                                private final TypeDescription.Generic targetType;
3515

3516
                                /**
3517
                                 * The index of the parameter.
3518
                                 */
3519
                                private final int index;
3520

3521
                                /**
3522
                                 * The typing to use when assigning.
3523
                                 */
3524
                                private final Assigner.Typing typing;
3525

3526
                                /**
3527
                                 * The source providing the argument.
3528
                                 */
3529
                                private final Source source;
3530

3531
                                /**
3532
                                 * {@code true} if {@code null} or a primitive {@code 0} should be assigned to the parameter
3533
                                 * if the provided index is not available.
3534
                                 */
3535
                                private final boolean optional;
3536

3537
                                /**
3538
                                 * The assigner to use.
3539
                                 */
3540
                                private final Assigner assigner;
3541

3542
                                /**
3543
                                 * The instrumented method.
3544
                                 */
3545
                                private final MethodDescription instrumentedMethod;
3546

3547
                                /**
3548
                                 * Creates a resolved offset mapping for assigning a parameter.
3549
                                 *
3550
                                 * @param targetType         The targeted type.
3551
                                 * @param index              The index of the parameter.
3552
                                 * @param typing             The typing to use when assigning.
3553
                                 * @param source             The source providing the argument.
3554
                                 * @param optional           {@code true} if {@code null} or a primitive {@code 0} should be assigned
3555
                                 *                           to the parameter if the provided index is not available.
3556
                                 * @param assigner           The assigner to use.
3557
                                 * @param instrumentedMethod The instrumented method.
3558
                                 */
3559
                                protected Resolved(TypeDescription.Generic targetType,
3560
                                                   int index,
3561
                                                   Assigner.Typing typing,
3562
                                                   Source source,
3563
                                                   boolean optional,
3564
                                                   Assigner assigner,
3565
                                                   MethodDescription instrumentedMethod) {
1✔
3566
                                    this.targetType = targetType;
1✔
3567
                                    this.index = index;
1✔
3568
                                    this.typing = typing;
1✔
3569
                                    this.source = source;
1✔
3570
                                    this.optional = optional;
1✔
3571
                                    this.assigner = assigner;
1✔
3572
                                    this.instrumentedMethod = instrumentedMethod;
1✔
3573
                                }
1✔
3574

3575
                                /**
3576
                                 * {@inheritDoc}
3577
                                 */
3578
                                public StackManipulation apply(TypeDescription receiver,
3579
                                                               ByteCodeElement.Member original,
3580
                                                               TypeList.Generic parameters,
3581
                                                               TypeDescription.Generic result,
3582
                                                               TypeDescription.Generic current,
3583
                                                               JavaConstant.MethodHandle methodHandle,
3584
                                                               Map<Integer, Integer> offsets,
3585
                                                               int offset) {
3586
                                    Source.Value value = source.argument(index, parameters, offsets, original, instrumentedMethod);
1✔
3587
                                    if (value != null) {
1✔
3588
                                        StackManipulation assignment = assigner.assign(value.getTypeDescription(), targetType, typing);
1✔
3589
                                        if (!assignment.isValid()) {
1✔
3590
                                            throw new IllegalStateException("Cannot assign " + value.getTypeDescription() + " to " + targetType);
×
3591
                                        }
3592
                                        return new StackManipulation.Compound(MethodVariableAccess.of(value.getTypeDescription()).loadFrom(value.getOffset()), assignment);
1✔
3593
                                    } else if (optional) {
1✔
3594
                                        return DefaultValue.of(targetType);
1✔
3595
                                    } else {
3596
                                        throw new IllegalStateException("No argument with index " + index + " available for " + original);
1✔
3597
                                    }
3598
                                }
3599
                            }
3600
                        }
3601

3602
                        /**
3603
                         * An offset mapping that assigns the {@code this} reference.
3604
                         */
3605
                        @HashCodeAndEqualsPlugin.Enhance
3606
                        class ForThisReference implements OffsetMapping {
3607

3608
                            /**
3609
                             * The targeted type.
3610
                             */
3611
                            private final TypeDescription.Generic targetType;
3612

3613
                            /**
3614
                             * The typing to use or {@code null} if implicit typing.
3615
                             */
3616
                            @MaybeNull
3617
                            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
3618
                            private final Assigner.Typing typing;
3619

3620
                            /**
3621
                             * The source providing the reference.
3622
                             */
3623
                            private final Source source;
3624

3625
                            /**
3626
                             * {@code true} if {@code null} or a primitive {@code 0} should be assigned to the parameter
3627
                             * if no {@code this} reference is available.
3628
                             */
3629
                            private final boolean optional;
3630

3631
                            /**
3632
                             * Creates an offset mapping that resolves the {@code this} reference.
3633
                             *
3634
                             * @param targetType The targeted type.
3635
                             * @param typing     The typing to use or {@code null} if implicit typing.
3636
                             * @param source     The source providing the reference.
3637
                             * @param optional   {@code true} if {@code null} or a primitive {@code 0} should be assigned
3638
                             *                   to the parameter if no {@code this} reference is available.
3639
                             */
3640
                            public ForThisReference(TypeDescription.Generic targetType, @MaybeNull Assigner.Typing typing, Source source, boolean optional) {
1✔
3641
                                this.targetType = targetType;
1✔
3642
                                this.typing = typing;
1✔
3643
                                this.source = source;
1✔
3644
                                this.optional = optional;
1✔
3645
                            }
1✔
3646

3647
                            /**
3648
                             * {@inheritDoc}
3649
                             */
3650
                            public ForThisReference.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
3651
                                return new ForThisReference.Resolved(targetType, this.typing == null ? typing : this.typing, source, optional, assigner, instrumentedMethod);
1✔
3652
                            }
3653

3654
                            /**
3655
                             * A resolved offset mapping for resolving the {@code this} reference.
3656
                             */
3657
                            @HashCodeAndEqualsPlugin.Enhance
3658
                            protected static class Resolved implements OffsetMapping.Resolved {
3659

3660
                                /**
3661
                                 * The targeted type.
3662
                                 */
3663
                                private final TypeDescription.Generic targetType;
3664

3665
                                /**
3666
                                 * The typing to use..
3667
                                 */
3668
                                private final Assigner.Typing typing;
3669

3670
                                /**
3671
                                 * The source providing the reference.
3672
                                 */
3673
                                private final Source source;
3674

3675
                                /**
3676
                                 * {@code true} if {@code null} or a primitive {@code 0} should be assigned to the parameter
3677
                                 * if no {@code this} reference is available.
3678
                                 */
3679
                                private final boolean optional;
3680

3681
                                /**
3682
                                 * The assigner to use.
3683
                                 */
3684
                                private final Assigner assigner;
3685

3686
                                /**
3687
                                 * The instrumented method.
3688
                                 */
3689
                                private final MethodDescription instrumentedMethod;
3690

3691
                                /**
3692
                                 * Creates a resolved offset mapping for assigning the {@code this} reference.
3693
                                 *
3694
                                 * @param targetType         The targeted type.
3695
                                 * @param typing             The typing to use.
3696
                                 * @param source             The source providing the reference.
3697
                                 * @param optional           {@code true} if {@code null} or a primitive {@code 0} should be assigned
3698
                                 *                           to the parameter if no {@code this} reference is available.
3699
                                 * @param assigner           The assigner to use.
3700
                                 * @param instrumentedMethod The instrumented method.
3701
                                 */
3702
                                protected Resolved(TypeDescription.Generic targetType,
3703
                                                   Assigner.Typing typing,
3704
                                                   Source source,
3705
                                                   boolean optional,
3706
                                                   Assigner assigner,
3707
                                                   MethodDescription instrumentedMethod) {
1✔
3708
                                    this.targetType = targetType;
1✔
3709
                                    this.typing = typing;
1✔
3710
                                    this.source = source;
1✔
3711
                                    this.optional = optional;
1✔
3712
                                    this.assigner = assigner;
1✔
3713
                                    this.instrumentedMethod = instrumentedMethod;
1✔
3714
                                }
1✔
3715

3716
                                /**
3717
                                 * {@inheritDoc}
3718
                                 */
3719
                                public StackManipulation apply(TypeDescription receiver,
3720
                                                               ByteCodeElement.Member original,
3721
                                                               TypeList.Generic parameters,
3722
                                                               TypeDescription.Generic result,
3723
                                                               TypeDescription.Generic current,
3724
                                                               JavaConstant.MethodHandle methodHandle,
3725
                                                               Map<Integer, Integer> offsets,
3726
                                                               int offset) {
3727
                                    Source.Value value = source.self(parameters, offsets, original, instrumentedMethod);
1✔
3728
                                    if (value != null) {
1✔
3729
                                        StackManipulation assignment = assigner.assign(value.getTypeDescription(), targetType, typing);
1✔
3730
                                        if (!assignment.isValid()) {
1✔
3731
                                            throw new IllegalStateException("Cannot assign " + value.getTypeDescription() + " to " + targetType);
×
3732
                                        }
3733
                                        return new StackManipulation.Compound(MethodVariableAccess.of(value.getTypeDescription()).loadFrom(value.getOffset()), assignment);
1✔
3734
                                    } else if (optional) {
1✔
3735
                                        return DefaultValue.of(targetType);
1✔
3736
                                    } else {
3737
                                        throw new IllegalStateException("No this reference available for " + original);
1✔
3738
                                    }
3739
                                }
3740
                            }
3741

3742
                            /**
3743
                             * A factory for creating an offset mapping for binding a {@link This} reference.
3744
                             */
3745
                            protected enum Factory implements OffsetMapping.Factory<This> {
1✔
3746

3747
                                /**
3748
                                 * The singleton instance.
3749
                                 */
3750
                                INSTANCE;
1✔
3751

3752
                                /**
3753
                                 * The {@link This#typing()} property.
3754
                                 */
3755
                                private static final MethodDescription.InDefinedShape THIS_TYPING;
3756

3757
                                /**
3758
                                 * The {@link This#source()} reference.
3759
                                 */
3760
                                private static final MethodDescription.InDefinedShape THIS_SOURCE;
3761

3762
                                /**
3763
                                 * The {@link This#optional()} property.
3764
                                 */
3765
                                private static final MethodDescription.InDefinedShape THIS_OPTIONAL;
3766

3767
                                /*
3768
                                 * Resolves the annotation properties.
3769
                                 */
3770
                                static {
3771
                                    MethodList<MethodDescription.InDefinedShape> methods = net.bytebuddy.description.type.TypeDescription.ForLoadedType.of(This.class).getDeclaredMethods();
1✔
3772
                                    THIS_TYPING = methods.filter(named("typing")).getOnly();
1✔
3773
                                    THIS_SOURCE = methods.filter(named("source")).getOnly();
1✔
3774
                                    THIS_OPTIONAL = methods.filter(named("optional")).getOnly();
1✔
3775
                                }
1✔
3776

3777
                                /**
3778
                                 * {@inheritDoc}
3779
                                 */
3780
                                public Class<This> getAnnotationType() {
3781
                                    return This.class;
1✔
3782
                                }
3783

3784
                                /**
3785
                                 * {@inheritDoc}
3786
                                 */
3787
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<This> annotation) {
3788
                                    return new ForThisReference(target.getDeclaringType().asGenericType(),
×
3789
                                            annotation.getValue(THIS_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class),
×
3790
                                            annotation.getValue(THIS_SOURCE).resolve(EnumerationDescription.class).load(Source.class),
×
3791
                                            annotation.getValue(THIS_OPTIONAL).resolve(Boolean.class));
×
3792
                                }
3793

3794
                                /**
3795
                                 * {@inheritDoc}
3796
                                 */
3797
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<This> annotation) {
3798
                                    return new ForThisReference(target.getType(),
1✔
3799
                                            annotation.getValue(THIS_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class),
1✔
3800
                                            annotation.getValue(THIS_SOURCE).resolve(EnumerationDescription.class).load(Source.class),
1✔
3801
                                            annotation.getValue(THIS_OPTIONAL).resolve(Boolean.class));
1✔
3802
                                }
3803
                            }
3804
                        }
3805

3806
                        /**
3807
                         * An offset mapping that assigns an array containing all arguments to the annotated parameter.
3808
                         */
3809
                        @HashCodeAndEqualsPlugin.Enhance
3810
                        class ForAllArguments implements OffsetMapping {
3811

3812
                            /**
3813
                             * The component type of the annotated parameter.
3814
                             */
3815
                            private final TypeDescription.Generic targetComponentType;
3816

3817
                            /**
3818
                             * The typing to use or {@code null} if implicit typing.
3819
                             */
3820
                            @MaybeNull
3821
                            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
3822
                            private final Assigner.Typing typing;
3823

3824
                            /**
3825
                             * The source providing the reference.
3826
                             */
3827
                            private final Source source;
3828

3829
                            /**
3830
                             * {@code true} if the {@code this} reference should be included in the created array, if available.
3831
                             */
3832
                            private final boolean includeSelf;
3833

3834
                            /**
3835
                             * {@code true} if {@code null} should be assigned to the parameter if no arguments are available.
3836
                             */
3837
                            private final boolean nullIfEmpty;
3838

3839
                            /**
3840
                             * Creates a new offset mapping for an array containing all supplied arguments.
3841
                             *
3842
                             * @param targetComponentType The component type of the annotated parameter.
3843
                             * @param typing              The typing to use or {@code null} if implicit typing.
3844
                             * @param source              The source providing the reference.
3845
                             * @param includeSelf         {@code true} if the {@code this} reference should be included in the created array, if available.
3846
                             * @param nullIfEmpty         {@code true} if {@code null} should be assigned to the parameter if no arguments are available.
3847
                             */
3848
                            public ForAllArguments(TypeDescription.Generic targetComponentType, @MaybeNull Assigner.Typing typing, Source source, boolean includeSelf, boolean nullIfEmpty) {
1✔
3849
                                this.targetComponentType = targetComponentType;
1✔
3850
                                this.typing = typing;
1✔
3851
                                this.source = source;
1✔
3852
                                this.includeSelf = includeSelf;
1✔
3853
                                this.nullIfEmpty = nullIfEmpty;
1✔
3854
                            }
1✔
3855

3856
                            /**
3857
                             * {@inheritDoc}
3858
                             */
3859
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
3860
                                return new ForAllArguments.Resolved(targetComponentType, this.typing == null ? typing : this.typing, source, includeSelf, nullIfEmpty, assigner, instrumentedMethod);
1✔
3861
                            }
3862

3863
                            /**
3864
                             * A factory for creating an offset mapping containing all supplies arguments.
3865
                             */
3866
                            protected enum Factory implements OffsetMapping.Factory<AllArguments> {
1✔
3867

3868
                                /**
3869
                                 * The singleton instance.
3870
                                 */
3871
                                INSTANCE;
1✔
3872

3873
                                /**
3874
                                 * The {@link AllArguments#typing()} property.
3875
                                 */
3876
                                private static final MethodDescription.InDefinedShape ALL_ARGUMENTS_TYPING;
3877

3878
                                /**
3879
                                 * The {@link AllArguments#source()} property.
3880
                                 */
3881
                                private static final MethodDescription.InDefinedShape ALL_ARGUMENTS_SOURCE;
3882

3883
                                /**
3884
                                 * The {@link AllArguments#includeSelf()} property.
3885
                                 */
3886
                                private static final MethodDescription.InDefinedShape ALL_ARGUMENTS_INCLUDE_SELF;
3887

3888
                                /**
3889
                                 * The {@link AllArguments#nullIfEmpty()} property.
3890
                                 */
3891
                                private static final MethodDescription.InDefinedShape ALL_ARGUMENTS_NULL_IF_EMPTY;
3892

3893
                                /*
3894
                                 * Resolves all annotation properties.
3895
                                 */
3896
                                static {
3897
                                    MethodList<MethodDescription.InDefinedShape> methods = TypeDescription.ForLoadedType.of(AllArguments.class).getDeclaredMethods();
1✔
3898
                                    ALL_ARGUMENTS_TYPING = methods.filter(named("typing")).getOnly();
1✔
3899
                                    ALL_ARGUMENTS_SOURCE = methods.filter(named("source")).getOnly();
1✔
3900
                                    ALL_ARGUMENTS_INCLUDE_SELF = methods.filter(named("includeSelf")).getOnly();
1✔
3901
                                    ALL_ARGUMENTS_NULL_IF_EMPTY = methods.filter(named("nullIfEmpty")).getOnly();
1✔
3902
                                }
1✔
3903

3904
                                /**
3905
                                 * {@inheritDoc}
3906
                                 */
3907
                                public Class<AllArguments> getAnnotationType() {
3908
                                    return AllArguments.class;
1✔
3909
                                }
3910

3911
                                /**
3912
                                 * {@inheritDoc}
3913
                                 */
3914
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<AllArguments> annotation) {
3915
                                    throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
3916
                                }
3917

3918
                                /**
3919
                                 * {@inheritDoc}
3920
                                 */
3921
                                @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Assuming component type for array type.")
3922
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<AllArguments> annotation) {
3923
                                    if (!target.getType().isArray()) {
1✔
3924
                                        throw new IllegalStateException("Expected array as parameter type for " + target);
1✔
3925
                                    }
3926
                                    return new ForAllArguments(target.getType().getComponentType(),
1✔
3927
                                            annotation.getValue(ALL_ARGUMENTS_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class),
1✔
3928
                                            annotation.getValue(ALL_ARGUMENTS_SOURCE).resolve(EnumerationDescription.class).load(Source.class),
1✔
3929
                                            annotation.getValue(ALL_ARGUMENTS_INCLUDE_SELF).resolve(Boolean.class),
1✔
3930
                                            annotation.getValue(ALL_ARGUMENTS_NULL_IF_EMPTY).resolve(Boolean.class));
1✔
3931
                                }
3932
                            }
3933

3934
                            /**
3935
                             * A resolves offset mapping for an array containing all arguments.
3936
                             */
3937
                            @HashCodeAndEqualsPlugin.Enhance
3938
                            protected static class Resolved implements OffsetMapping.Resolved {
3939

3940
                                /**
3941
                                 * The component type of the annotated parameter.
3942
                                 */
3943
                                private final TypeDescription.Generic targetComponentType;
3944

3945
                                /**
3946
                                 * The typing to use.
3947
                                 */
3948
                                private final Assigner.Typing typing;
3949

3950
                                /**
3951
                                 * The source providing the reference.
3952
                                 */
3953
                                private final Source source;
3954

3955
                                /**
3956
                                 * {@code true} if the {@code this} reference should be included in the created array, if available.
3957
                                 */
3958
                                private final boolean includeSelf;
3959

3960
                                /**
3961
                                 * {@code true} if {@code null} should be assigned to the parameter if no arguments are available.
3962
                                 */
3963
                                private final boolean nullIfEmpty;
3964

3965
                                /**
3966
                                 * The assigner to use.
3967
                                 */
3968
                                private final Assigner assigner;
3969

3970
                                /**
3971
                                 * The instrumented method.
3972
                                 */
3973
                                private final MethodDescription instrumentedMethod;
3974

3975
                                /**
3976
                                 * Creates a resolved version for an offset mapping of all arguments.
3977
                                 *
3978
                                 * @param targetComponentType The component type of the annotated parameter.
3979
                                 * @param typing              The typing to use.
3980
                                 * @param source              The source providing the reference.
3981
                                 * @param includeSelf         {@code true} if the {@code this} reference should be included in the created array, if available.
3982
                                 * @param nullIfEmpty         {@code true} if {@code null} should be assigned to the parameter if no arguments are available.
3983
                                 * @param assigner            The assigner to use.
3984
                                 * @param instrumentedMethod  The instrumented method.
3985
                                 */
3986
                                protected Resolved(TypeDescription.Generic targetComponentType,
3987
                                                   Assigner.Typing typing,
3988
                                                   Source source,
3989
                                                   boolean includeSelf,
3990
                                                   boolean nullIfEmpty,
3991
                                                   Assigner assigner,
3992
                                                   MethodDescription instrumentedMethod) {
1✔
3993
                                    this.targetComponentType = targetComponentType;
1✔
3994
                                    this.typing = typing;
1✔
3995
                                    this.source = source;
1✔
3996
                                    this.includeSelf = includeSelf;
1✔
3997
                                    this.nullIfEmpty = nullIfEmpty;
1✔
3998
                                    this.assigner = assigner;
1✔
3999
                                    this.instrumentedMethod = instrumentedMethod;
1✔
4000
                                }
1✔
4001

4002
                                /**
4003
                                 * {@inheritDoc}
4004
                                 */
4005
                                public StackManipulation apply(TypeDescription receiver,
4006
                                                               ByteCodeElement.Member original,
4007
                                                               TypeList.Generic parameters,
4008
                                                               TypeDescription.Generic result,
4009
                                                               TypeDescription.Generic current,
4010
                                                               JavaConstant.MethodHandle methodHandle,
4011
                                                               Map<Integer, Integer> offsets,
4012
                                                               int offset) {
4013
                                    List<Source.Value> values = source.arguments(includeSelf, parameters, offsets, original, instrumentedMethod);
1✔
4014
                                    if (nullIfEmpty && values.isEmpty()) {
1✔
4015
                                        return NullConstant.INSTANCE;
1✔
4016
                                    } else {
4017
                                        List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>();
1✔
4018
                                        for (Source.Value value : values) {
1✔
4019
                                            StackManipulation assignment = assigner.assign(value.getTypeDescription(), targetComponentType, typing);
1✔
4020
                                            if (!assignment.isValid()) {
1✔
4021
                                                throw new IllegalStateException("Cannot assign " + value.getTypeDescription() + " to " + targetComponentType);
×
4022
                                            }
4023
                                            stackManipulations.add(new StackManipulation.Compound(MethodVariableAccess.of(value.getTypeDescription()).loadFrom(value.getOffset()), assignment));
1✔
4024
                                        }
1✔
4025
                                        return ArrayFactory.forType(targetComponentType).withValues(stackManipulations);
1✔
4026
                                    }
4027
                                }
4028
                            }
4029
                        }
4030

4031
                        /**
4032
                         * An offset mapping resolving a method handle to invoke the original expression or the instrumented method.
4033
                         */
4034
                        @HashCodeAndEqualsPlugin.Enhance
4035
                        class ForSelfCallHandle implements OffsetMapping {
4036

4037
                            /**
4038
                             * The source providing the reference.
4039
                             */
4040
                            private final Source source;
4041

4042
                            /**
4043
                             * {@code true} if the handle should be bound to the original arguments.
4044
                             */
4045
                            private final boolean bound;
4046

4047
                            /**
4048
                             * Creates a new offset mapping for a self call handle.
4049
                             *
4050
                             * @param source The source providing the reference.
4051
                             * @param bound  {@code true} if the handle should be bound to the original arguments.
4052
                             */
4053
                            public ForSelfCallHandle(Source source, boolean bound) {
×
4054
                                this.source = source;
×
4055
                                this.bound = bound;
×
4056
                            }
×
4057

4058
                            /**
4059
                             * {@inheritDoc}
4060
                             */
4061
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4062
                                return bound ? new ForSelfCallHandle.Bound(source, instrumentedMethod) : new ForSelfCallHandle.Unbound(source, instrumentedMethod);
×
4063
                            }
4064

4065
                            /**
4066
                             * A factory for creating an offset mapping for binding a self call handle.
4067
                             */
4068
                            protected enum Factory implements OffsetMapping.Factory<SelfCallHandle> {
1✔
4069

4070
                                /**
4071
                                 * The singleton instance.
4072
                                 */
4073
                                INSTANCE;
1✔
4074

4075
                                /**
4076
                                 * The {@link SelfCallHandle#source()} property.
4077
                                 */
4078
                                private static final MethodDescription.InDefinedShape ALL_ARGUMENTS_SOURCE;
4079

4080
                                /**
4081
                                 * The {@link SelfCallHandle#bound()} property.
4082
                                 */
4083
                                private static final MethodDescription.InDefinedShape ALL_ARGUMENTS_BOUND;
4084

4085
                                /*
4086
                                 * Resolves all annotation properties.
4087
                                 */
4088
                                static {
4089
                                    MethodList<MethodDescription.InDefinedShape> methods = TypeDescription.ForLoadedType.of(SelfCallHandle.class).getDeclaredMethods();
1✔
4090
                                    ALL_ARGUMENTS_SOURCE = methods.filter(named("source")).getOnly();
1✔
4091
                                    ALL_ARGUMENTS_BOUND = methods.filter(named("bound")).getOnly();
1✔
4092
                                }
1✔
4093

4094
                                /**
4095
                                 * {@inheritDoc}
4096
                                 */
4097
                                public Class<SelfCallHandle> getAnnotationType() {
4098
                                    return SelfCallHandle.class;
1✔
4099
                                }
4100

4101
                                /**
4102
                                 * {@inheritDoc}
4103
                                 */
4104
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<SelfCallHandle> annotation) {
4105
                                    throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
4106
                                }
4107

4108
                                /**
4109
                                 * {@inheritDoc}
4110
                                 */
4111
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<SelfCallHandle> annotation) {
4112
                                    if (!target.getType().asErasure().isAssignableFrom(JavaType.METHOD_HANDLE.getTypeStub())) {
×
4113
                                        throw new IllegalStateException("Cannot assign method handle to " + target);
×
4114
                                    }
4115
                                    return new ForSelfCallHandle(
×
4116
                                            annotation.getValue(ALL_ARGUMENTS_SOURCE).resolve(EnumerationDescription.class).load(Source.class),
×
4117
                                            annotation.getValue(ALL_ARGUMENTS_BOUND).resolve(Boolean.class));
×
4118
                                }
4119
                            }
4120

4121
                            /**
4122
                             * Resolves a bound self call handle for an offset mapping.
4123
                             */
4124
                            @HashCodeAndEqualsPlugin.Enhance
4125
                            protected static class Bound implements OffsetMapping.Resolved {
4126

4127
                                /**
4128
                                 * The source providing the reference.
4129
                                 */
4130
                                private final Source source;
4131

4132
                                /**
4133
                                 * The instrumented method.
4134
                                 */
4135
                                private final MethodDescription instrumentedMethod;
4136

4137
                                /**
4138
                                 * Creates an offset mapping for a bound version of a self call handle.
4139
                                 *
4140
                                 * @param source             The source providing the reference.
4141
                                 * @param instrumentedMethod The instrumented method.
4142
                                 */
4143
                                protected Bound(Source source, MethodDescription instrumentedMethod) {
×
4144
                                    this.source = source;
×
4145
                                    this.instrumentedMethod = instrumentedMethod;
×
4146
                                }
×
4147

4148
                                /**
4149
                                 * {@inheritDoc}
4150
                                 */
4151
                                public StackManipulation apply(TypeDescription receiver,
4152
                                                               ByteCodeElement.Member original,
4153
                                                               TypeList.Generic parameters,
4154
                                                               TypeDescription.Generic result,
4155
                                                               TypeDescription.Generic current,
4156
                                                               JavaConstant.MethodHandle methodHandle,
4157
                                                               Map<Integer, Integer> offsets,
4158
                                                               int offset) {
4159
                                    Source.Value dispatched = source.self(parameters, offsets, original, instrumentedMethod);
×
4160
                                    List<Source.Value> values = source.arguments(false, parameters, offsets, original, instrumentedMethod);
×
4161
                                    List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(1 + (values.size()
×
4162
                                            + (dispatched == null ? 0 : 2))
4163
                                            + (values.isEmpty() ? 0 : 1));
×
4164
                                    stackManipulations.add(source.handle(methodHandle, instrumentedMethod).toStackManipulation());
×
4165
                                    if (dispatched != null) {
×
4166
                                        stackManipulations.add(MethodVariableAccess.of(dispatched.getTypeDescription()).loadFrom(dispatched.getOffset()));
×
4167
                                        stackManipulations.add(MethodInvocation.invoke(new MethodDescription.Latent(JavaType.METHOD_HANDLE.getTypeStub(), new MethodDescription.Token("bindTo",
×
4168
                                                Opcodes.ACC_PUBLIC,
4169
                                                JavaType.METHOD_HANDLE.getTypeStub().asGenericType(),
×
4170
                                                new TypeList.Generic.Explicit(TypeDefinition.Sort.describe(Object.class))))));
×
4171
                                    }
4172
                                    if (!values.isEmpty()) {
×
4173
                                        for (Source.Value value : values) {
×
4174
                                            stackManipulations.add(MethodVariableAccess.of(value.getTypeDescription()).loadFrom(value.getOffset()));
×
4175
                                        }
×
4176
                                        stackManipulations.add(MethodInvocation.invoke(new MethodDescription.Latent(JavaType.METHOD_HANDLES.getTypeStub(), new MethodDescription.Token("insertArguments",
×
4177
                                                Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
4178
                                                JavaType.METHOD_HANDLE.getTypeStub().asGenericType(),
×
4179
                                                new TypeList.Generic.Explicit(JavaType.METHOD_HANDLE.getTypeStub(), TypeDefinition.Sort.describe(int.class), TypeDefinition.Sort.describe(Object[].class))))));
×
4180
                                    }
4181
                                    return new StackManipulation.Compound(stackManipulations);
×
4182
                                }
4183
                            }
4184

4185
                            /**
4186
                             * Resolves an unbound self call handle for an offset mapping.
4187
                             */
4188
                            @HashCodeAndEqualsPlugin.Enhance
4189
                            protected static class Unbound implements OffsetMapping.Resolved {
4190

4191
                                /**
4192
                                 * The source providing the reference.
4193
                                 */
4194
                                private final Source source;
4195

4196
                                /**
4197
                                 * The instrumented method.
4198
                                 */
4199
                                private final MethodDescription instrumentedMethod;
4200

4201
                                /**
4202
                                 * Creates an offset mapping for an unbound version of a self call handle.
4203
                                 *
4204
                                 * @param source             The source providing the reference.
4205
                                 * @param instrumentedMethod The instrumented method.
4206
                                 */
4207
                                protected Unbound(Source source, MethodDescription instrumentedMethod) {
×
4208
                                    this.source = source;
×
4209
                                    this.instrumentedMethod = instrumentedMethod;
×
4210
                                }
×
4211

4212
                                /**
4213
                                 * {@inheritDoc}
4214
                                 */
4215
                                public StackManipulation apply(TypeDescription receiver,
4216
                                                               ByteCodeElement.Member original,
4217
                                                               TypeList.Generic parameters,
4218
                                                               TypeDescription.Generic result,
4219
                                                               TypeDescription.Generic current,
4220
                                                               JavaConstant.MethodHandle methodHandle,
4221
                                                               Map<Integer, Integer> offsets,
4222
                                                               int offset) {
4223
                                    return source.handle(methodHandle, instrumentedMethod).toStackManipulation();
×
4224
                                }
4225
                            }
4226
                        }
4227

4228
                        /**
4229
                         * An offset mapping for a field value.
4230
                         */
4231
                        @HashCodeAndEqualsPlugin.Enhance
4232
                        abstract class ForField implements OffsetMapping {
4233

4234
                            /**
4235
                             * The {@link FieldValue#value()} property.
4236
                             */
4237
                            private static final MethodDescription.InDefinedShape FIELD_VALUE_VALUE;
4238

4239
                            /**
4240
                             * The {@link FieldValue#declaringType()} property.
4241
                             */
4242
                            private static final MethodDescription.InDefinedShape FIELD_VALUE_DECLARING_TYPE;
4243

4244
                            /**
4245
                             * The {@link FieldValue#typing()} property.
4246
                             */
4247
                            private static final MethodDescription.InDefinedShape FIELD_VALUE_TYPING;
4248

4249
                            /*
4250
                             * Resolves all annotation properties.
4251
                             */
4252
                            static {
4253
                                MethodList<MethodDescription.InDefinedShape> methods = TypeDescription.ForLoadedType.of(FieldValue.class).getDeclaredMethods();
1✔
4254
                                FIELD_VALUE_VALUE = methods.filter(named("value")).getOnly();
1✔
4255
                                FIELD_VALUE_DECLARING_TYPE = methods.filter(named("declaringType")).getOnly();
1✔
4256
                                FIELD_VALUE_TYPING = methods.filter(named("typing")).getOnly();
1✔
4257
                            }
1✔
4258

4259
                            /**
4260
                             * A description of the targeted type.
4261
                             */
4262
                            private final TypeDescription.Generic target;
4263

4264
                            /**
4265
                             * The typing to use or {@code null} if implicit typing.
4266
                             */
4267
                            @MaybeNull
4268
                            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
4269
                            private final Assigner.Typing typing;
4270

4271
                            /**
4272
                             * Creates an offset mapping for a field value.
4273
                             *
4274
                             * @param target A description of the targeted type.
4275
                             * @param typing The typing to use or {@code null} if implicit typing.
4276
                             */
4277
                            protected ForField(TypeDescription.Generic target, @MaybeNull Assigner.Typing typing) {
1✔
4278
                                this.target = target;
1✔
4279
                                this.typing = typing;
1✔
4280
                            }
1✔
4281

4282
                            /**
4283
                             * {@inheritDoc}
4284
                             */
4285
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4286
                                FieldDescription fieldDescription = resolve(instrumentedType, instrumentedMethod);
1✔
4287
                                if (!fieldDescription.isStatic() && instrumentedMethod.isStatic()) {
1✔
4288
                                    throw new IllegalStateException("Cannot access non-static field " + fieldDescription + " from static method " + instrumentedMethod);
×
4289
                                }
4290
                                StackManipulation assignment = assigner.assign(fieldDescription.getType(), target, this.typing == null ? typing : this.typing);
1✔
4291
                                if (!assignment.isValid()) {
1✔
4292
                                    throw new IllegalStateException("Cannot assign " + fieldDescription + " to " + target);
×
4293
                                }
4294
                                return new OffsetMapping.Resolved.ForStackManipulation(new StackManipulation.Compound(fieldDescription.isStatic()
1✔
4295
                                        ? StackManipulation.Trivial.INSTANCE
4296
                                        : MethodVariableAccess.loadThis(), FieldAccess.forField(fieldDescription).read(), assignment));
1✔
4297
                            }
4298

4299
                            /**
4300
                             * Resolves a description of the field being accessed.
4301
                             *
4302
                             * @param instrumentedType   The instrumented type.
4303
                             * @param instrumentedMethod The instrumented method.
4304
                             * @return A description of the field being accessed.
4305
                             */
4306
                            protected abstract FieldDescription resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod);
4307

4308
                            /**
4309
                             * An offset mapping for an unresolved field value.
4310
                             */
4311
                            @HashCodeAndEqualsPlugin.Enhance
4312
                            public abstract static class Unresolved extends ForField {
4313

4314
                                /**
4315
                                 * Indicates that the name of the field should be inferred from the instrumented method's name as a bean property.
4316
                                 */
4317
                                protected static final String BEAN_PROPERTY = "";
4318

4319
                                /**
4320
                                 * The name of the field being accessed or an empty string if the name of the field should be inferred.
4321
                                 */
4322
                                private final String name;
4323

4324
                                /**
4325
                                 * Creates an offset mapping for the value of an unresolved field.
4326
                                 *
4327
                                 * @param target A description of the targeted type.
4328
                                 * @param typing The typing to use.
4329
                                 * @param name   The name of the field being accessed or an empty string if the name of the field should be inferred.
4330
                                 */
4331
                                protected Unresolved(TypeDescription.Generic target, Assigner.Typing typing, String name) {
4332
                                    super(target, typing);
1✔
4333
                                    this.name = name;
1✔
4334
                                }
1✔
4335

4336
                                @Override
4337
                                protected FieldDescription resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4338
                                    FieldLocator locator = fieldLocator(instrumentedType);
1✔
4339
                                    FieldLocator.Resolution resolution = name.equals(BEAN_PROPERTY)
1✔
4340
                                            ? FieldLocator.Resolution.Simple.ofBeanAccessor(locator, instrumentedMethod)
1✔
4341
                                            : locator.locate(name);
1✔
4342
                                    if (!resolution.isResolved()) {
1✔
4343
                                        throw new IllegalStateException("Cannot locate field named " + name + " for " + instrumentedType);
×
4344
                                    } else {
4345
                                        return resolution.getField();
1✔
4346
                                    }
4347
                                }
4348

4349
                                /**
4350
                                 * Creates a field locator for the instrumented type.
4351
                                 *
4352
                                 * @param instrumentedType The instrumented type.
4353
                                 * @return An appropriate field locator.
4354
                                 */
4355
                                protected abstract FieldLocator fieldLocator(TypeDescription instrumentedType);
4356

4357
                                /**
4358
                                 * An offset mapping for an unresolved field with an implicit declaring type.
4359
                                 */
4360
                                public static class WithImplicitType extends Unresolved {
4361

4362
                                    /**
4363
                                     * Creates an offset mapping for an unresolved field value with an implicit declaring type.
4364
                                     *
4365
                                     * @param target     A description of the targeted type.
4366
                                     * @param annotation The annotation describing the access.
4367
                                     */
4368
                                    protected WithImplicitType(TypeDescription.Generic target, AnnotationDescription.Loadable<FieldValue> annotation) {
4369
                                        this(target,
1✔
4370
                                                annotation.getValue(FIELD_VALUE_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class),
1✔
4371
                                                annotation.getValue(FIELD_VALUE_VALUE).resolve(String.class));
1✔
4372
                                    }
1✔
4373

4374
                                    /**
4375
                                     * Creates an offset mapping for the value of an unresolved field with an implicit declaring type.
4376
                                     *
4377
                                     * @param target A description of the targeted type.
4378
                                     * @param typing The typing to use.
4379
                                     * @param name   The name of the field being accessed or an empty string if the name of the field should be inferred.
4380
                                     */
4381
                                    public WithImplicitType(TypeDescription.Generic target, Assigner.Typing typing, String name) {
4382
                                        super(target, typing, name);
1✔
4383
                                    }
1✔
4384

4385
                                    @Override
4386
                                    protected FieldLocator fieldLocator(TypeDescription instrumentedType) {
4387
                                        return new FieldLocator.ForClassHierarchy(instrumentedType);
1✔
4388
                                    }
4389
                                }
4390

4391
                                /**
4392
                                 * An offset mapping for an unresolved field value with an explicit declaring type.
4393
                                 */
4394
                                @HashCodeAndEqualsPlugin.Enhance
4395
                                public static class WithExplicitType extends Unresolved {
4396

4397
                                    /**
4398
                                     * The field's declaring type.
4399
                                     */
4400
                                    private final TypeDescription declaringType;
4401

4402
                                    /**
4403
                                     * Creates an offset mapping for the value of an unresolved field with an explicit declaring type.
4404
                                     *
4405
                                     * @param target        A description of the targeted type.
4406
                                     * @param annotation    The annotation describing the field access.
4407
                                     * @param declaringType The field's declaring type.
4408
                                     */
4409
                                    protected WithExplicitType(TypeDescription.Generic target, AnnotationDescription.Loadable<FieldValue> annotation, TypeDescription declaringType) {
4410
                                        this(target,
1✔
4411
                                                annotation.getValue(FIELD_VALUE_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class),
1✔
4412
                                                annotation.getValue(FIELD_VALUE_VALUE).resolve(String.class),
1✔
4413
                                                declaringType);
4414
                                    }
1✔
4415

4416
                                    /**
4417
                                     * Creates an offset mapping for the value of an unresolved field with an explicit declaring type.
4418
                                     *
4419
                                     * @param target        A description of the targeted type.
4420
                                     * @param typing        The typing to use.
4421
                                     * @param name          The name of the field being accessed or an empty string if the name of the field should be inferred.
4422
                                     * @param declaringType The field's declaring type.
4423
                                     */
4424
                                    public WithExplicitType(TypeDescription.Generic target, Assigner.Typing typing, String name, TypeDescription declaringType) {
4425
                                        super(target, typing, name);
1✔
4426
                                        this.declaringType = declaringType;
1✔
4427
                                    }
1✔
4428

4429
                                    @Override
4430
                                    protected FieldLocator fieldLocator(TypeDescription instrumentedType) {
4431
                                        if (!declaringType.represents(TargetType.class) && !instrumentedType.isAssignableTo(declaringType)) {
1✔
4432
                                            throw new IllegalStateException(declaringType + " is no super type of " + instrumentedType);
×
4433
                                        }
4434
                                        return new FieldLocator.ForExactType(TargetType.resolve(declaringType, instrumentedType));
1✔
4435
                                    }
4436
                                }
4437

4438
                                /**
4439
                                 * A factory for creating an offset mapping for a field value.
4440
                                 */
4441
                                protected enum Factory implements OffsetMapping.Factory<FieldValue> {
1✔
4442

4443
                                    /**
4444
                                     * The singleton instance.
4445
                                     */
4446
                                    INSTANCE;
1✔
4447

4448
                                    /**
4449
                                     * {@inheritDoc}
4450
                                     */
4451
                                    public Class<FieldValue> getAnnotationType() {
4452
                                        return FieldValue.class;
1✔
4453
                                    }
4454

4455
                                    /**
4456
                                     * {@inheritDoc}
4457
                                     */
4458
                                    public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<FieldValue> annotation) {
4459
                                        TypeDescription declaringType = annotation.getValue(FIELD_VALUE_DECLARING_TYPE).resolve(TypeDescription.class);
×
4460
                                        return declaringType.represents(void.class)
×
4461
                                                ? new Unresolved.WithImplicitType(target.getDeclaringType().asGenericType(), annotation)
×
4462
                                                : new Unresolved.WithExplicitType(target.getDeclaringType().asGenericType(), annotation, declaringType);
×
4463
                                    }
4464

4465
                                    /**
4466
                                     * {@inheritDoc}
4467
                                     */
4468
                                    public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<FieldValue> annotation) {
4469
                                        TypeDescription declaringType = annotation.getValue(FIELD_VALUE_DECLARING_TYPE).resolve(TypeDescription.class);
1✔
4470
                                        return declaringType.represents(void.class)
1✔
4471
                                                ? new Unresolved.WithImplicitType(target.getType(), annotation)
1✔
4472
                                                : new Unresolved.WithExplicitType(target.getType(), annotation, declaringType);
1✔
4473
                                    }
4474
                                }
4475
                            }
4476

4477
                            /**
4478
                             * An offset mapping for a resolved field access.
4479
                             */
4480
                            @HashCodeAndEqualsPlugin.Enhance
4481
                            public static class Resolved extends ForField {
4482

4483
                                /**
4484
                                 * A description of the field being accessed.
4485
                                 */
4486
                                private final FieldDescription fieldDescription;
4487

4488
                                /**
4489
                                 * Creates a resolved offset mapping for a field access.
4490
                                 *
4491
                                 * @param target           A description of the targeted type.
4492
                                 * @param typing           The typing to use or {@code null} if implicit typing.
4493
                                 * @param fieldDescription A description of the field accessed.
4494
                                 */
4495
                                public Resolved(TypeDescription.Generic target, Assigner.Typing typing, FieldDescription fieldDescription) {
4496
                                    super(target, typing);
1✔
4497
                                    this.fieldDescription = fieldDescription;
1✔
4498
                                }
1✔
4499

4500
                                @Override
4501
                                @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Assuming declaring type for type member.")
4502
                                protected FieldDescription resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4503
                                    if (!fieldDescription.isStatic() && !fieldDescription.getDeclaringType().asErasure().isAssignableFrom(instrumentedType)) {
1✔
4504
                                        throw new IllegalStateException(fieldDescription + " is no member of " + instrumentedType);
×
4505
                                    } else if (!fieldDescription.isVisibleTo(instrumentedType)) {
1✔
4506
                                        throw new IllegalStateException("Cannot access " + fieldDescription + " from " + instrumentedType);
×
4507
                                    }
4508
                                    return fieldDescription;
1✔
4509
                                }
4510

4511
                                /**
4512
                                 * A factory for creating a resolved offset mapping of a field value.
4513
                                 *
4514
                                 * @param <T> The type of the annotation.
4515
                                 */
4516
                                @HashCodeAndEqualsPlugin.Enhance
4517
                                public static class Factory<T extends Annotation> extends OffsetMapping.Factory.AbstractBase<T> {
4518

4519
                                    /**
4520
                                     * The annotation type.
4521
                                     */
4522
                                    private final Class<T> annotationType;
4523

4524
                                    /**
4525
                                     * The field being accessed.
4526
                                     */
4527
                                    private final FieldDescription fieldDescription;
4528

4529
                                    /**
4530
                                     * The typing to use.
4531
                                     */
4532
                                    private final Assigner.Typing typing;
4533

4534
                                    /**
4535
                                     * Creates a factory for reading a given field.
4536
                                     *
4537
                                     * @param annotationType   The annotation type.
4538
                                     * @param fieldDescription The field being accessed.
4539
                                     */
4540
                                    public Factory(Class<T> annotationType, FieldDescription fieldDescription) {
4541
                                        this(annotationType, fieldDescription, Assigner.Typing.STATIC);
1✔
4542
                                    }
1✔
4543

4544
                                    /**
4545
                                     * Creates a factory for reading a given field.
4546
                                     *
4547
                                     * @param annotationType   The annotation type.
4548
                                     * @param fieldDescription The field being accessed.
4549
                                     * @param typing           The typing to use.
4550
                                     */
4551
                                    public Factory(Class<T> annotationType, FieldDescription fieldDescription, Assigner.Typing typing) {
1✔
4552
                                        this.annotationType = annotationType;
1✔
4553
                                        this.fieldDescription = fieldDescription;
1✔
4554
                                        this.typing = typing;
1✔
4555
                                    }
1✔
4556

4557
                                    /**
4558
                                     * {@inheritDoc}
4559
                                     */
4560
                                    public Class<T> getAnnotationType() {
4561
                                        return annotationType;
1✔
4562
                                    }
4563

4564
                                    @Override
4565
                                    protected OffsetMapping make(TypeDescription.Generic target, AnnotationDescription.Loadable<T> annotation) {
4566
                                        return new ForField.Resolved(target, typing, fieldDescription);
1✔
4567
                                    }
4568
                                }
4569
                            }
4570
                        }
4571

4572
                        /**
4573
                         * An offset mapping for a method handle representing a field getter or setter.
4574
                         */
4575
                        @HashCodeAndEqualsPlugin.Enhance
4576
                        abstract class ForFieldHandle implements OffsetMapping {
4577

4578
                            /**
4579
                             * The type of access to the field.
4580
                             */
4581
                            private final Access access;
4582

4583
                            /**
4584
                             * Creates an offset mapping for a field getter or setter.
4585
                             *
4586
                             * @param access The type of access to the field.
4587
                             */
4588
                            protected ForFieldHandle(Access access) {
×
4589
                                this.access = access;
×
4590
                            }
×
4591

4592
                            /**
4593
                             * {@inheritDoc}
4594
                             */
4595
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4596
                                FieldDescription fieldDescription = resolve(instrumentedType, instrumentedMethod);
×
4597
                                if (!fieldDescription.isStatic() && instrumentedMethod.isStatic()) {
×
4598
                                    throw new IllegalStateException("Cannot access non-static field " + fieldDescription + " from static method " + instrumentedMethod);
×
4599
                                }
4600
                                if (fieldDescription.isStatic()) {
×
4601
                                    return new OffsetMapping.Resolved.ForStackManipulation(access.resolve(fieldDescription.asDefined()).toStackManipulation());
×
4602
                                } else {
4603
                                    return new OffsetMapping.Resolved.ForStackManipulation(new StackManipulation.Compound(
×
4604
                                            access.resolve(fieldDescription.asDefined()).toStackManipulation(), MethodVariableAccess.REFERENCE.loadFrom(THIS_REFERENCE),
×
4605
                                            MethodInvocation.invoke(new MethodDescription.Latent(JavaType.METHOD_HANDLE.getTypeStub(), new MethodDescription.Token("bindTo",
×
4606
                                                    Opcodes.ACC_PUBLIC,
4607
                                                    JavaType.METHOD_HANDLE.getTypeStub().asGenericType(),
×
4608
                                                    new TypeList.Generic.Explicit(TypeDefinition.Sort.describe(Object.class)))))));
×
4609
                                }
4610
                            }
4611

4612
                            /**
4613
                             * Resolves a description of the field being accessed.
4614
                             *
4615
                             * @param instrumentedType   The instrumented type.
4616
                             * @param instrumentedMethod The instrumented method.
4617
                             * @return A description of the field being accessed.
4618
                             */
4619
                            protected abstract FieldDescription resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod);
4620

4621
                            /**
4622
                             * The type of access to the field.
4623
                             */
4624
                            public enum Access {
×
4625

4626
                                /**
4627
                                 * Describes a field getter.
4628
                                 */
4629
                                GETTER {
×
4630
                                    @Override
4631
                                    protected JavaConstant.MethodHandle resolve(FieldDescription.InDefinedShape fieldDescription) {
4632
                                        return JavaConstant.MethodHandle.ofGetter(fieldDescription);
×
4633
                                    }
4634
                                },
4635

4636
                                /**
4637
                                 * Describes a field setter.
4638
                                 */
4639
                                SETTER {
×
4640
                                    @Override
4641
                                    protected JavaConstant.MethodHandle resolve(FieldDescription.InDefinedShape fieldDescription) {
4642
                                        return JavaConstant.MethodHandle.ofSetter(fieldDescription);
×
4643
                                    }
4644
                                };
4645

4646
                                /**
4647
                                 * Resolves a handle for the represented field access.
4648
                                 *
4649
                                 * @param fieldDescription The field that is being accessed.
4650
                                 * @return An appropriate method handle.
4651
                                 */
4652
                                protected abstract JavaConstant.MethodHandle resolve(FieldDescription.InDefinedShape fieldDescription);
4653
                            }
4654

4655
                            /**
4656
                             * An offset mapping for an unresolved field handle.
4657
                             */
4658
                            @HashCodeAndEqualsPlugin.Enhance
4659
                            public abstract static class Unresolved extends ForFieldHandle {
4660

4661
                                /**
4662
                                 * Indicates that the field's name should be resolved as a bean property.
4663
                                 */
4664
                                protected static final String BEAN_PROPERTY = "";
4665

4666
                                /**
4667
                                 * The name of the field or an empty string if the name should be resolved from the instrumented method.
4668
                                 */
4669
                                private final String name;
4670

4671
                                /**
4672
                                 * Creates an offset mapping for an unresolved field handle.
4673
                                 *
4674
                                 * @param access The type of access to the field.
4675
                                 * @param name   The name of the field or an empty string if the name should be resolved from the instrumented method.
4676
                                 */
4677
                                public Unresolved(Access access, String name) {
4678
                                    super(access);
×
4679
                                    this.name = name;
×
4680
                                }
×
4681

4682
                                @Override
4683
                                protected FieldDescription resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4684
                                    FieldLocator locator = fieldLocator(instrumentedType);
×
4685
                                    FieldLocator.Resolution resolution = name.equals(BEAN_PROPERTY)
×
4686
                                            ? FieldLocator.Resolution.Simple.ofBeanAccessor(locator, instrumentedMethod)
×
4687
                                            : locator.locate(name);
×
4688
                                    if (!resolution.isResolved()) {
×
4689
                                        throw new IllegalStateException("Cannot locate field named " + name + " for " + instrumentedType);
×
4690
                                    } else {
4691
                                        return resolution.getField();
×
4692
                                    }
4693
                                }
4694

4695
                                /**
4696
                                 * Resolves a field locator for the instrumented type.
4697
                                 *
4698
                                 * @param instrumentedType The instrumented type.
4699
                                 * @return Returns an appropriate field locator.
4700
                                 */
4701
                                protected abstract FieldLocator fieldLocator(TypeDescription instrumentedType);
4702

4703
                                /**
4704
                                 * An offset mapping for an unresolved field handle with an implicit declaring type.
4705
                                 */
4706
                                public static class WithImplicitType extends Unresolved {
4707

4708
                                    /**
4709
                                     * Creates an offset mapping for an unresolved field handle with an implicit declaring type.
4710
                                     *
4711
                                     * @param access The type of access to the field.
4712
                                     * @param name   The name of the field or an empty string if the name should be resolved from the instrumented method.
4713
                                     */
4714
                                    public WithImplicitType(Access access, String name) {
4715
                                        super(access, name);
×
4716
                                    }
×
4717

4718
                                    @Override
4719
                                    protected FieldLocator fieldLocator(TypeDescription instrumentedType) {
4720
                                        return new FieldLocator.ForClassHierarchy(instrumentedType);
×
4721
                                    }
4722
                                }
4723

4724
                                /**
4725
                                 * An offset mapping for an unresolved field handle with an explicit declaring type.
4726
                                 */
4727
                                @HashCodeAndEqualsPlugin.Enhance
4728
                                public static class WithExplicitType extends Unresolved {
4729

4730
                                    /**
4731
                                     * The field's declaring type.
4732
                                     */
4733
                                    private final TypeDescription declaringType;
4734

4735
                                    /**
4736
                                     * Creates an offset mapping for an unresolved field handle with an explicit declaring type.
4737
                                     *
4738
                                     * @param access        The type of access to the field.
4739
                                     * @param name          The name of the field or an empty string if the name should be resolved from the instrumented method.
4740
                                     * @param declaringType The field's declaring type.
4741
                                     */
4742
                                    public WithExplicitType(Access access, String name, TypeDescription declaringType) {
4743
                                        super(access, name);
×
4744
                                        this.declaringType = declaringType;
×
4745
                                    }
×
4746

4747
                                    @Override
4748
                                    protected FieldLocator fieldLocator(TypeDescription instrumentedType) {
4749
                                        if (!declaringType.represents(TargetType.class) && !instrumentedType.isAssignableTo(declaringType)) {
×
4750
                                            throw new IllegalStateException(declaringType + " is no super type of " + instrumentedType);
×
4751
                                        }
4752
                                        return new FieldLocator.ForExactType(TargetType.resolve(declaringType, instrumentedType));
×
4753
                                    }
4754
                                }
4755

4756
                                /**
4757
                                 * A factory for creating a method handle representing a getter for the targeted field.
4758
                                 */
4759
                                protected enum GetterFactory implements OffsetMapping.Factory<FieldGetterHandle> {
1✔
4760

4761
                                    /**
4762
                                     * The singleton instance.
4763
                                     */
4764
                                    INSTANCE;
1✔
4765

4766
                                    /**
4767
                                     * The {@link FieldGetterHandle#value()} method.
4768
                                     */
4769
                                    private static final MethodDescription.InDefinedShape FIELD_GETTER_HANDLE_VALUE;
4770

4771
                                    /**
4772
                                     * The {@link FieldGetterHandle#declaringType()} method.
4773
                                     */
4774
                                    private static final MethodDescription.InDefinedShape FIELD_GETTER_HANDLE_DECLARING_TYPE;
4775

4776
                                    /*
4777
                                     * Resolves all annotation properties.
4778
                                     */
4779
                                    static {
4780
                                        MethodList<MethodDescription.InDefinedShape> methods = TypeDescription.ForLoadedType.of(FieldGetterHandle.class).getDeclaredMethods();
1✔
4781
                                        FIELD_GETTER_HANDLE_VALUE = methods.filter(named("value")).getOnly();
1✔
4782
                                        FIELD_GETTER_HANDLE_DECLARING_TYPE = methods.filter(named("declaringType")).getOnly();
1✔
4783
                                    }
1✔
4784

4785
                                    /**
4786
                                     * {@inheritDoc}
4787
                                     */
4788
                                    public Class<FieldGetterHandle> getAnnotationType() {
4789
                                        return FieldGetterHandle.class;
1✔
4790
                                    }
4791

4792
                                    /**
4793
                                     * {@inheritDoc}
4794
                                     */
4795
                                    public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<FieldGetterHandle> annotation) {
4796
                                        throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
4797
                                    }
4798

4799
                                    /**
4800
                                     * {@inheritDoc}
4801
                                     */
4802
                                    public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<FieldGetterHandle> annotation) {
4803
                                        if (!target.getType().asErasure().isAssignableFrom(JavaType.METHOD_HANDLE.getTypeStub())) {
×
4804
                                            throw new IllegalStateException("Cannot assign method handle to " + target);
×
4805
                                        }
4806
                                        TypeDescription declaringType = annotation.getValue(FIELD_GETTER_HANDLE_DECLARING_TYPE).resolve(TypeDescription.class);
×
4807
                                        return declaringType.represents(void.class)
×
4808
                                                ? new ForFieldHandle.Unresolved.WithImplicitType(Access.GETTER, annotation.getValue(FIELD_GETTER_HANDLE_VALUE).resolve(String.class))
×
4809
                                                : new ForFieldHandle.Unresolved.WithExplicitType(Access.GETTER, annotation.getValue(FIELD_GETTER_HANDLE_VALUE).resolve(String.class), declaringType);
×
4810
                                    }
4811
                                }
4812

4813
                                /**
4814
                                 * A factory for creating a method handle representing a setter for the targeted field.
4815
                                 */
4816
                                protected enum SetterFactory implements OffsetMapping.Factory<FieldSetterHandle> {
1✔
4817

4818
                                    /**
4819
                                     * The singleton instance.
4820
                                     */
4821
                                    INSTANCE;
1✔
4822

4823
                                    /**
4824
                                     * The {@link FieldGetterHandle#value()} method.
4825
                                     */
4826
                                    private static final MethodDescription.InDefinedShape FIELD_SETTER_HANDLE_VALUE;
4827

4828
                                    /**
4829
                                     * The {@link FieldGetterHandle#declaringType()} method.
4830
                                     */
4831
                                    private static final MethodDescription.InDefinedShape FIELD_SETTER_HANDLE_DECLARING_TYPE;
4832

4833
                                    /*
4834
                                     * Resolves the annotation properties.
4835
                                     */
4836
                                    static {
4837
                                        MethodList<MethodDescription.InDefinedShape> methods = TypeDescription.ForLoadedType.of(FieldSetterHandle.class).getDeclaredMethods();
1✔
4838
                                        FIELD_SETTER_HANDLE_VALUE = methods.filter(named("value")).getOnly();
1✔
4839
                                        FIELD_SETTER_HANDLE_DECLARING_TYPE = methods.filter(named("declaringType")).getOnly();
1✔
4840
                                    }
1✔
4841

4842
                                    /**
4843
                                     * {@inheritDoc}
4844
                                     */
4845
                                    public Class<FieldSetterHandle> getAnnotationType() {
4846
                                        return FieldSetterHandle.class;
1✔
4847
                                    }
4848

4849
                                    /**
4850
                                     * {@inheritDoc}
4851
                                     */
4852
                                    public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<FieldSetterHandle> annotation) {
4853
                                        throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
4854
                                    }
4855

4856
                                    /**
4857
                                     * {@inheritDoc}
4858
                                     */
4859
                                    public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<FieldSetterHandle> annotation) {
4860
                                        if (!target.getType().asErasure().isAssignableFrom(JavaType.METHOD_HANDLE.getTypeStub())) {
×
4861
                                            throw new IllegalStateException("Cannot assign method handle to " + target);
×
4862
                                        }
4863
                                        TypeDescription declaringType = annotation.getValue(FIELD_SETTER_HANDLE_DECLARING_TYPE).resolve(TypeDescription.class);
×
4864
                                        return declaringType.represents(void.class)
×
4865
                                                ? new ForFieldHandle.Unresolved.WithImplicitType(Access.SETTER, annotation.getValue(FIELD_SETTER_HANDLE_VALUE).resolve(String.class))
×
4866
                                                : new ForFieldHandle.Unresolved.WithExplicitType(Access.SETTER, annotation.getValue(FIELD_SETTER_HANDLE_VALUE).resolve(String.class), declaringType);
×
4867
                                    }
4868
                                }
4869
                            }
4870

4871
                            /**
4872
                             * An offset mapping for a resolved field handle.
4873
                             */
4874
                            @HashCodeAndEqualsPlugin.Enhance
4875
                            public static class Resolved extends OffsetMapping.ForFieldHandle {
4876

4877
                                /**
4878
                                 * The field that is being accessed.
4879
                                 */
4880
                                private final FieldDescription fieldDescription;
4881

4882
                                /**
4883
                                 * Creates a resolved mapping for a field access handle.
4884
                                 *
4885
                                 * @param access           The type of access.
4886
                                 * @param fieldDescription The field that is being accessed.
4887
                                 */
4888
                                public Resolved(Access access, FieldDescription fieldDescription) {
4889
                                    super(access);
×
4890
                                    this.fieldDescription = fieldDescription;
×
4891
                                }
×
4892

4893
                                @Override
4894
                                @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Assuming declaring type for type member.")
4895
                                protected FieldDescription resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4896
                                    if (!fieldDescription.isStatic() && !fieldDescription.getDeclaringType().asErasure().isAssignableFrom(instrumentedType)) {
×
4897
                                        throw new IllegalStateException(fieldDescription + " is no member of " + instrumentedType);
×
4898
                                    } else if (!fieldDescription.isVisibleTo(instrumentedType)) {
×
4899
                                        throw new IllegalStateException("Cannot access " + fieldDescription + " from " + instrumentedType);
×
4900
                                    }
4901
                                    return fieldDescription;
×
4902
                                }
4903

4904
                                /**
4905
                                 * A factory to create an offset mapping for a resolved field handle.
4906
                                 *
4907
                                 * @param <T> The type of the annotation.
4908
                                 */
4909
                                @HashCodeAndEqualsPlugin.Enhance
4910
                                public static class Factory<T extends Annotation> implements OffsetMapping.Factory<T> {
4911

4912
                                    /**
4913
                                     * The annotation type.
4914
                                     */
4915
                                    private final Class<T> annotationType;
4916

4917
                                    /**
4918
                                     * The field being accessed.
4919
                                     */
4920
                                    private final FieldDescription fieldDescription;
4921

4922
                                    /**
4923
                                     * The type of access.
4924
                                     */
4925
                                    private final Access access;
4926

4927
                                    /**
4928
                                     * Creates a new factory for a field access handle.
4929
                                     *
4930
                                     * @param annotationType   The annotation type.
4931
                                     * @param fieldDescription The field being accessed.
4932
                                     * @param access           The type of access.
4933
                                     */
4934
                                    public Factory(Class<T> annotationType, FieldDescription fieldDescription, Access access) {
×
4935
                                        this.annotationType = annotationType;
×
4936
                                        this.fieldDescription = fieldDescription;
×
4937
                                        this.access = access;
×
4938
                                    }
×
4939

4940
                                    /**
4941
                                     * {@inheritDoc}
4942
                                     */
4943
                                    public Class<T> getAnnotationType() {
4944
                                        return annotationType;
×
4945
                                    }
4946

4947
                                    /**
4948
                                     * {@inheritDoc}
4949
                                     */
4950
                                    public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<T> annotation) {
4951
                                        throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
4952
                                    }
4953

4954
                                    /**
4955
                                     * {@inheritDoc}
4956
                                     */
4957
                                    public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<T> annotation) {
4958
                                        if (!target.getType().asErasure().isAssignableFrom(JavaType.METHOD_HANDLE.getTypeStub())) {
×
4959
                                            throw new IllegalStateException("Cannot assign method handle to " + target);
×
4960
                                        }
4961
                                        return new ForFieldHandle.Resolved(access, fieldDescription);
×
4962
                                    }
4963
                                }
4964
                            }
4965
                        }
4966

4967
                        /**
4968
                         * An offset mapping for describing a representation of the substituted element or the instrumented method.
4969
                         */
4970
                        @HashCodeAndEqualsPlugin.Enhance
4971
                        class ForOrigin implements OffsetMapping {
4972

4973
                            /**
4974
                             * The sort of the origin representation.
4975
                             */
4976
                            private final Sort sort;
4977

4978
                            /**
4979
                             * The source providing the reference.
4980
                             */
4981
                            private final Source source;
4982

4983
                            /**
4984
                             * Creates an offset mapping a representation of the substituted element or instrumented method.
4985
                             *
4986
                             * @param sort   The sort of the origin representation.
4987
                             * @param source The source providing the reference.
4988
                             */
4989
                            protected ForOrigin(Sort sort, Source source) {
1✔
4990
                                this.sort = sort;
1✔
4991
                                this.source = source;
1✔
4992
                            }
1✔
4993

4994
                            /**
4995
                             * {@inheritDoc}
4996
                             */
4997
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
4998
                                return new ForOrigin.Resolved(sort, source, instrumentedMethod);
1✔
4999
                            }
5000

5001
                            /**
5002
                             * The sort of the origin expression.
5003
                             */
5004
                            protected enum Sort {
1✔
5005

5006
                                /**
5007
                                 * Represents the supplied value as a {@link Method}.
5008
                                 */
5009
                                METHOD {
1✔
5010
                                    @Override
5011
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5012
                                        return original instanceof MethodDescription && ((MethodDescription) original).isMethod();
1✔
5013
                                    }
5014

5015
                                    @Override
5016
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5017
                                        return MethodConstant.of(((MethodDescription) original).asDefined());
1✔
5018
                                    }
5019
                                },
5020

5021
                                /**
5022
                                 * Represents the supplied value as a {@link Constructor}.
5023
                                 */
5024
                                CONSTRUCTOR {
1✔
5025
                                    @Override
5026
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5027
                                        return original instanceof MethodDescription && ((MethodDescription) original).isConstructor();
×
5028
                                    }
5029

5030
                                    @Override
5031
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5032
                                        return MethodConstant.of(((MethodDescription) original).asDefined());
×
5033
                                    }
5034
                                },
5035

5036
                                /**
5037
                                 * Represents the supplied value as a {@link Field}.
5038
                                 */
5039
                                FIELD {
1✔
5040
                                    @Override
5041
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5042
                                        return original instanceof FieldDescription;
1✔
5043
                                    }
5044

5045
                                    @Override
5046
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5047
                                        return new FieldConstant(((FieldDescription) original).asDefined());
1✔
5048
                                    }
5049
                                },
5050

5051
                                /**
5052
                                 * Represents the supplied value as a {@code java.lang.reflect.Executable}.
5053
                                 */
5054
                                EXECUTABLE {
1✔
5055
                                    @Override
5056
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5057
                                        return original instanceof MethodDescription;
×
5058
                                    }
5059

5060
                                    @Override
5061
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5062
                                        return MethodConstant.of(((MethodDescription) original).asDefined());
×
5063
                                    }
5064
                                },
5065

5066
                                /**
5067
                                 * Represents the supplied value as a {@link Class}.
5068
                                 */
5069
                                TYPE {
1✔
5070
                                    @Override
5071
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5072
                                        return true;
1✔
5073
                                    }
5074

5075
                                    @Override
5076
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5077
                                        return ClassConstant.of(original.getDeclaringType().asErasure());
1✔
5078
                                    }
5079
                                },
5080

5081
                                /**
5082
                                 * Represents the supplied value as a {@code java.lang.invoke.MethodHandles.Lookup}.
5083
                                 */
5084
                                LOOKUP {
1✔
5085
                                    @Override
5086
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5087
                                        return true;
1✔
5088
                                    }
5089

5090
                                    @Override
5091
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5092
                                        return MethodInvocation.lookup();
1✔
5093
                                    }
5094
                                },
5095

5096
                                /**
5097
                                 * Represents the supplied value as a {@code java.lang.invoke.MethodHandle}.
5098
                                 */
5099
                                METHOD_HANDLE {
1✔
5100
                                    @Override
5101
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5102
                                        return true;
1✔
5103
                                    }
5104

5105
                                    @Override
5106
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5107
                                        JavaConstant.MethodHandle handle;
5108
                                        if (original instanceof MethodDescription) {
1✔
5109
                                            handle = JavaConstant.MethodHandle.of(((MethodDescription) original).asDefined());
1✔
5110
                                        } else if (original instanceof FieldDescription) {
1✔
5111
                                            handle = returnType.represents(void.class)
1✔
5112
                                                    ? JavaConstant.MethodHandle.ofSetter(((FieldDescription) original).asDefined())
1✔
5113
                                                    : JavaConstant.MethodHandle.ofGetter(((FieldDescription) original).asDefined());
1✔
5114
                                        } else {
5115
                                            throw new IllegalStateException("Unexpected byte code element: " + original);
×
5116
                                        }
5117
                                        return handle.toStackManipulation();
1✔
5118
                                    }
5119
                                },
5120

5121
                                /**
5122
                                 * Represents the supplied value as a {@code java.lang.invoke.MethodType}.
5123
                                 */
5124
                                METHOD_TYPE {
1✔
5125
                                    @Override
5126
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5127
                                        return true;
1✔
5128
                                    }
5129

5130
                                    @Override
5131
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5132
                                        return JavaConstant.MethodType.of(returnType, parameterTypes).toStackManipulation();
1✔
5133
                                    }
5134
                                },
5135

5136
                                /**
5137
                                 * Represents the supplied value as its {@link Object#toString()} representation.
5138
                                 */
5139
                                STRING {
1✔
5140
                                    @Override
5141
                                    protected boolean isRepresentable(ByteCodeElement.Member original) {
5142
                                        return true;
1✔
5143
                                    }
5144

5145
                                    @Override
5146
                                    protected StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType) {
5147
                                        return new TextConstant(original.toString());
1✔
5148
                                    }
5149
                                };
5150

5151
                                /**
5152
                                 * Checks if the supplied member can be represented by this sort.
5153
                                 *
5154
                                 * @param original The byte code element to check.
5155
                                 * @return {@code true} if the supplied element can be represented.
5156
                                 */
5157
                                protected abstract boolean isRepresentable(ByteCodeElement.Member original);
5158

5159
                                /**
5160
                                 * Creates a stack manipulation for the supplied byte code element.
5161
                                 *
5162
                                 * @param original       The substituted element.
5163
                                 * @param parameterTypes The parameter types.
5164
                                 * @param returnType     The return type.
5165
                                 * @return A stack manipulation loading the supplied byte code element's representation onto the stack.
5166
                                 */
5167
                                protected abstract StackManipulation resolve(ByteCodeElement.Member original, List<TypeDescription> parameterTypes, TypeDescription returnType);
5168
                            }
5169

5170
                            /**
5171
                             * A factory for an offset mapping that describes a representation of the substituted element or instrumented method.
5172
                             */
5173
                            protected enum Factory implements OffsetMapping.Factory<Origin> {
1✔
5174

5175
                                /**
5176
                                 * The singleton instance.
5177
                                 */
5178
                                INSTANCE;
1✔
5179

5180
                                /**
5181
                                 * The {@link Origin#source()} property.
5182
                                 */
5183
                                private static final MethodDescription.InDefinedShape ORIGIN_TYPE = TypeDescription.ForLoadedType.of(Origin.class)
1✔
5184
                                        .getDeclaredMethods()
1✔
5185
                                        .filter(named("source"))
1✔
5186
                                        .getOnly();
1✔
5187

5188
                                /**
5189
                                 * {@inheritDoc}
5190
                                 */
5191
                                public Class<Origin> getAnnotationType() {
5192
                                    return Origin.class;
1✔
5193
                                }
5194

5195
                                /**
5196
                                 * {@inheritDoc}
5197
                                 */
5198
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<Origin> annotation) {
5199
                                    throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
5200
                                }
5201

5202
                                /**
5203
                                 * {@inheritDoc}
5204
                                 */
5205
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<Origin> annotation) {
5206
                                    Sort sort;
5207
                                    if (target.getType().asErasure().represents(Class.class)) {
1✔
5208
                                        sort = Sort.TYPE;
1✔
5209
                                    } else if (target.getType().asErasure().represents(Method.class)) {
1✔
5210
                                        sort = Sort.METHOD;
1✔
5211
                                    } else if (target.getType().asErasure().represents(Constructor.class)) {
1✔
5212
                                        sort = Sort.CONSTRUCTOR;
×
5213
                                    } else if (target.getType().asErasure().represents(Field.class)) {
1✔
5214
                                        sort = Sort.FIELD;
1✔
5215
                                    } else if (JavaType.EXECUTABLE.getTypeStub().equals(target.getType().asErasure())) {
1✔
5216
                                        sort = Sort.EXECUTABLE;
×
5217
                                    } else if (JavaType.METHOD_HANDLE.getTypeStub().equals(target.getType().asErasure())) {
1✔
5218
                                        sort = Sort.METHOD_HANDLE;
1✔
5219
                                    } else if (JavaType.METHOD_TYPE.getTypeStub().equals(target.getType().asErasure())) {
1✔
5220
                                        sort = Sort.METHOD_TYPE;
1✔
5221
                                    } else if (JavaType.METHOD_HANDLES_LOOKUP.getTypeStub().equals(target.getType().asErasure())) {
1✔
5222
                                        sort = Sort.LOOKUP;
1✔
5223
                                    } else if (target.getType().asErasure().isAssignableFrom(String.class)) {
1✔
5224
                                        sort = Sort.STRING;
1✔
5225
                                    } else {
5226
                                        throw new IllegalStateException("Non-supported type " + target.getType() + " for @Origin annotation");
×
5227
                                    }
5228
                                    return new ForOrigin(sort, annotation.getValue(ORIGIN_TYPE).resolve(EnumerationDescription.class).load(Source.class));
1✔
5229
                                }
5230
                            }
5231

5232
                            /**
5233
                             * A resolved offset mapping for a representation of the substituted expression or instrumented method.
5234
                             */
5235
                            @HashCodeAndEqualsPlugin.Enhance
5236
                            protected static class Resolved implements OffsetMapping.Resolved {
5237

5238
                                /**
5239
                                 * The sort of the origin representation.
5240
                                 */
5241
                                private final Sort sort;
5242

5243
                                /**
5244
                                 * The source providing the reference.
5245
                                 */
5246
                                private final Source source;
5247

5248
                                /**
5249
                                 * The instrumented method.
5250
                                 */
5251
                                private final MethodDescription instrumentedMethod;
5252

5253
                                /**
5254
                                 * Creates a resolved version of an offset mapping for describing the substituted expression or instrumented method.
5255
                                 *
5256
                                 * @param sort               The sort of the origin representation.
5257
                                 * @param source             The source providing the reference.
5258
                                 * @param instrumentedMethod The instrumented method.
5259
                                 */
5260
                                protected Resolved(Sort sort, Source source, MethodDescription instrumentedMethod) {
1✔
5261
                                    this.sort = sort;
1✔
5262
                                    this.source = source;
1✔
5263
                                    this.instrumentedMethod = instrumentedMethod;
1✔
5264
                                }
1✔
5265

5266
                                /**
5267
                                 * {@inheritDoc}
5268
                                 */
5269
                                public StackManipulation apply(TypeDescription receiver,
5270
                                                               ByteCodeElement.Member original,
5271
                                                               TypeList.Generic parameters,
5272
                                                               TypeDescription.Generic result,
5273
                                                               TypeDescription.Generic current,
5274
                                                               JavaConstant.MethodHandle methodHandle,
5275
                                                               Map<Integer, Integer> offsets,
5276
                                                               int offset) {
5277
                                    if (!source.isRepresentable(sort, original, instrumentedMethod)) {
1✔
5278
                                        throw new IllegalStateException("Cannot represent " + sort + " for " + source + " in " + instrumentedMethod);
×
5279
                                    }
5280
                                    return source.resolve(sort, original, parameters, result, instrumentedMethod);
1✔
5281
                                }
5282
                            }
5283
                        }
5284

5285
                        /**
5286
                         * An offset mapping that assigns a stub value.
5287
                         */
5288
                        @HashCodeAndEqualsPlugin.Enhance
5289
                        class ForStubValue implements OffsetMapping {
5290

5291
                            /**
5292
                             * The source providing the reference.
5293
                             */
5294
                            private final Source source;
5295

5296
                            /**
5297
                             * Creates an offset mapping for a stub value.
5298
                             *
5299
                             * @param source The source providing the reference.
5300
                             */
5301
                            protected ForStubValue(Source source) {
1✔
5302
                                this.source = source;
1✔
5303
                            }
1✔
5304

5305
                            /**
5306
                             * {@inheritDoc}
5307
                             */
5308
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
5309
                                return new Resolved(source, instrumentedMethod);
1✔
5310
                            }
5311

5312
                            /**
5313
                             * A resolved offset mapping for an offset mapping of a stub value.
5314
                             */
5315
                            @HashCodeAndEqualsPlugin.Enhance
5316
                            protected static class Resolved implements OffsetMapping.Resolved {
5317

5318
                                /**
5319
                                 * The source providing the reference.
5320
                                 */
5321
                                private final Source source;
5322

5323
                                /**
5324
                                 * The instrumented method.
5325
                                 */
5326
                                private final MethodDescription instrumentedMethod;
5327

5328
                                /**
5329
                                 * Creates a resolved version of an offset mapping for a stub value.
5330
                                 *
5331
                                 * @param source             The source providing the reference.
5332
                                 * @param instrumentedMethod The instrumented method.
5333
                                 */
5334
                                protected Resolved(Source source, MethodDescription instrumentedMethod) {
1✔
5335
                                    this.source = source;
1✔
5336
                                    this.instrumentedMethod = instrumentedMethod;
1✔
5337
                                }
1✔
5338

5339
                                /**
5340
                                 * {@inheritDoc}
5341
                                 */
5342
                                public StackManipulation apply(TypeDescription receiver,
5343
                                                               ByteCodeElement.Member original,
5344
                                                               TypeList.Generic parameters,
5345
                                                               TypeDescription.Generic result,
5346
                                                               TypeDescription.Generic current,
5347
                                                               JavaConstant.MethodHandle methodHandle,
5348
                                                               Map<Integer, Integer> offsets,
5349
                                                               int offset) {
5350
                                    return DefaultValue.of(source.handle(methodHandle, instrumentedMethod).getReturnType());
1✔
5351
                                }
5352
                            }
5353

5354
                            /**
5355
                             * A factory for creating an offset mapping for a stub value.
5356
                             */
5357
                            @HashCodeAndEqualsPlugin.Enhance
1✔
5358
                            protected enum Factory implements OffsetMapping.Factory<StubValue> {
5359

5360
                                /**
5361
                                 * The singleton instance.
5362
                                 */
5363
                                INSTANCE;
1✔
5364

5365
                                /**
5366
                                 * The {@link StubValue#source()} property.
5367
                                 */
5368
                                private static final MethodDescription.InDefinedShape STUB_VALUE_SOURCE = TypeDescription.ForLoadedType.of(StubValue.class)
1✔
5369
                                        .getDeclaredMethods()
1✔
5370
                                        .filter(named("source"))
1✔
5371
                                        .getOnly();
1✔
5372

5373
                                /**
5374
                                 * {@inheritDoc}
5375
                                 */
5376
                                public Class<StubValue> getAnnotationType() {
5377
                                    return StubValue.class;
1✔
5378
                                }
5379

5380
                                /**
5381
                                 * {@inheritDoc}
5382
                                 */
5383
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<StubValue> annotation) {
5384
                                    throw new UnsupportedOperationException("This factory does not support binding a method receiver");
×
5385
                                }
5386

5387
                                /**
5388
                                 * {@inheritDoc}
5389
                                 */
5390
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<StubValue> annotation) {
5391
                                    if (!target.getType().represents(Object.class)) {
1✔
5392
                                        throw new IllegalStateException("Expected " + target + " to declare an Object type");
1✔
5393
                                    }
5394
                                    return new ForStubValue(annotation.getValue(STUB_VALUE_SOURCE).resolve(EnumerationDescription.class).load(Source.class));
1✔
5395
                                }
5396
                            }
5397
                        }
5398

5399
                        /**
5400
                         * An offset mapping that assigns the value of the previous chain instruction.
5401
                         */
5402
                        @HashCodeAndEqualsPlugin.Enhance
5403
                        class ForCurrent implements OffsetMapping {
5404

5405
                            /**
5406
                             * The type of the targeted expression.
5407
                             */
5408
                            private final TypeDescription.Generic targetType;
5409

5410
                            /**
5411
                             * The typing to use or {@code null} if implicit typing.
5412
                             */
5413
                            @MaybeNull
5414
                            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
5415
                            private final Assigner.Typing typing;
5416

5417
                            /**
5418
                             * Creates an offset mapping for the previous chain instruction.
5419
                             *
5420
                             * @param targetType The type of the targeted expression.
5421
                             * @param typing     The typing to use or {@code null} if implicit typing.
5422
                             */
5423
                            public ForCurrent(TypeDescription.Generic targetType, @MaybeNull Assigner.Typing typing) {
1✔
5424
                                this.targetType = targetType;
1✔
5425
                                this.typing = typing;
1✔
5426
                            }
1✔
5427

5428
                            /**
5429
                             * {@inheritDoc}
5430
                             */
5431
                            public OffsetMapping.Resolved resolve(Assigner assigner, Assigner.Typing typing, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
5432
                                return new ForCurrent.Resolved(targetType, assigner, this.typing == null ? typing : this.typing);
1✔
5433
                            }
5434

5435
                            /**
5436
                             * A factory for creating an offset mapping for assigning the result of the previous chain instruction.
5437
                             */
5438
                            protected enum Factory implements OffsetMapping.Factory<Current> {
1✔
5439

5440
                                /**
5441
                                 * The singleton instance.
5442
                                 */
5443
                                INSTANCE;
1✔
5444

5445
                                /**
5446
                                 * The {@link Current#typing()} property.
5447
                                 */
5448
                                private static final MethodDescription.InDefinedShape CURRENT_TYPING = TypeDescription.ForLoadedType.of(Current.class)
1✔
5449
                                        .getDeclaredMethods()
1✔
5450
                                        .filter(named("typing"))
1✔
5451
                                        .getOnly();
1✔
5452

5453
                                /**
5454
                                 * {@inheritDoc}
5455
                                 */
5456
                                public Class<Current> getAnnotationType() {
5457
                                    return Current.class;
1✔
5458
                                }
5459

5460
                                /**
5461
                                 * {@inheritDoc}
5462
                                 */
5463
                                public OffsetMapping make(MethodDescription.InDefinedShape target, AnnotationDescription.Loadable<Current> annotation) {
5464
                                    return new ForCurrent(target.getDeclaringType().asGenericType(),
×
5465
                                            annotation.getValue(CURRENT_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class));
×
5466
                                }
5467

5468
                                /**
5469
                                 * {@inheritDoc}
5470
                                 */
5471
                                public OffsetMapping make(ParameterDescription.InDefinedShape target, AnnotationDescription.Loadable<Current> annotation) {
5472
                                    return new ForCurrent(target.getType(), annotation.getValue(CURRENT_TYPING).resolve(EnumerationDescription.class).load(Assigner.Typing.class));
1✔
5473
                                }
5474
                            }
5475

5476
                            /**
5477
                             * A resolved offset mapping for assigning the previous chain instruction.
5478
                             */
5479
                            @HashCodeAndEqualsPlugin.Enhance
5480
                            protected static class Resolved implements OffsetMapping.Resolved {
5481

5482
                                /**
5483
                                 * The type of the targeted expression.
5484
                                 */
5485
                                private final TypeDescription.Generic targetType;
5486

5487
                                /**
5488
                                 * The assigner to use.
5489
                                 */
5490
                                private final Assigner assigner;
5491

5492
                                /**
5493
                                 * The typing to use.
5494
                                 */
5495
                                private final Assigner.Typing typing;
5496

5497
                                /**
5498
                                 * Creates a resolved offset mapping for assigning the previous chain instruction.
5499
                                 *
5500
                                 * @param targetType The type of the targeted expression.
5501
                                 * @param assigner   The assigner to use.
5502
                                 * @param typing     The typing to use.
5503
                                 */
5504
                                public Resolved(TypeDescription.Generic targetType, Assigner assigner, Assigner.Typing typing) {
1✔
5505
                                    this.targetType = targetType;
1✔
5506
                                    this.assigner = assigner;
1✔
5507
                                    this.typing = typing;
1✔
5508
                                }
1✔
5509

5510
                                /**
5511
                                 * {@inheritDoc}
5512
                                 */
5513
                                public StackManipulation apply(TypeDescription receiver,
5514
                                                               ByteCodeElement.Member original,
5515
                                                               TypeList.Generic parameters,
5516
                                                               TypeDescription.Generic result,
5517
                                                               TypeDescription.Generic current,
5518
                                                               JavaConstant.MethodHandle methodHandle,
5519
                                                               Map<Integer, Integer> offsets,
5520
                                                               int offset) {
5521
                                    StackManipulation assignment = assigner.assign(current, targetType, typing);
1✔
5522
                                    if (!assignment.isValid()) {
1✔
5523
                                        throw new IllegalStateException("Cannot assign " + current + " to " + targetType);
×
5524
                                    }
5525
                                    return new StackManipulation.Compound(MethodVariableAccess.of(current).loadFrom(offset), assignment);
1✔
5526
                                }
5527
                            }
5528
                        }
5529
                    }
5530

5531
                    /**
5532
                     * A dispatcher for invoking a delegation method.
5533
                     */
5534
                    protected interface Dispatcher {
5535

5536
                        /**
5537
                         * Resolves a dispatcher for a given instrumented type and method.
5538
                         *
5539
                         * @param instrumentedType   The instrumented type.
5540
                         * @param instrumentedMethod The instrumented method.
5541
                         * @return A resolved version of this dispatcher.
5542
                         */
5543
                        Dispatcher.Resolved resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod);
5544

5545
                        /**
5546
                         * A dispatcher that has been resolved for a given instrumented type and method.
5547
                         */
5548
                        interface Resolved {
5549

5550
                            StackManipulation initialize();
5551

5552
                            /**
5553
                             * Creates a stack manipulation for a given substitution target.
5554
                             *
5555
                             * @param receiver     The type upon which the substituted element is invoked upon.
5556
                             * @param original     The substituted element.
5557
                             * @param methodHandle A method handle that describes the invocation.
5558
                             * @return A stack manipulation that executes the represented delegation.
5559
                             */
5560
                            StackManipulation apply(TypeDescription receiver, ByteCodeElement.Member original, JavaConstant.MethodHandle methodHandle);
5561
                        }
5562

5563
                        /**
5564
                         * A factory for creating a dispatcher.
5565
                         */
5566
                        interface Factory {
5567

5568
                            /**
5569
                             * Creates a dispatcher for a given delegation method.
5570
                             *
5571
                             * @param delegate The method or constructor to delegate to.
5572
                             * @return An appropriate dispatcher.
5573
                             */
5574
                            Dispatcher make(MethodDescription.InDefinedShape delegate);
5575
                        }
5576

5577
                        /**
5578
                         * A dispatcher that invokes a delegate method directly.
5579
                         */
5580
                        @HashCodeAndEqualsPlugin.Enhance
5581
                        class ForRegularInvocation implements Dispatcher, Dispatcher.Resolved {
5582

5583
                            /**
5584
                             * The delegation method.
5585
                             */
5586
                            private final MethodDescription delegate;
5587

5588
                            /**
5589
                             * Creates a dispatcher for a regular method invocation.
5590
                             *
5591
                             * @param delegate The delegation method.
5592
                             */
5593
                            protected ForRegularInvocation(MethodDescription delegate) {
1✔
5594
                                this.delegate = delegate;
1✔
5595
                            }
1✔
5596

5597
                            /**
5598
                             * {@inheritDoc}
5599
                             */
5600
                            public Resolved resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
5601
                                return this;
1✔
5602
                            }
5603

5604
                            /**
5605
                             * {@inheritDoc}
5606
                             */
5607
                            public StackManipulation initialize() {
5608
                                return delegate.isConstructor()
1✔
5609
                                        ? new StackManipulation.Compound(TypeCreation.of(delegate.getDeclaringType().asErasure()), Duplication.SINGLE)
1✔
5610
                                        : StackManipulation.Trivial.INSTANCE;
5611
                            }
5612

5613
                            /**
5614
                             * {@inheritDoc}
5615
                             */
5616
                            public StackManipulation apply(TypeDescription receiver, ByteCodeElement.Member original, JavaConstant.MethodHandle methodHandle) {
5617
                                return MethodInvocation.invoke(delegate);
1✔
5618
                            }
5619

5620
                            /**
5621
                             * A factory for creating a dispatcher for a regular method invocation.
5622
                             */
5623
                            protected enum Factory implements Dispatcher.Factory {
1✔
5624

5625
                                /**
5626
                                 * The singleton instance.
5627
                                 */
5628
                                INSTANCE;
1✔
5629

5630
                                /**
5631
                                 * {@inheritDoc}
5632
                                 */
5633
                                public Dispatcher make(MethodDescription.InDefinedShape delegate) {
5634
                                    return new ForRegularInvocation(delegate);
1✔
5635
                                }
5636
                            }
5637
                        }
5638

5639
                        /**
5640
                         * A method dispatcher that is using a dynamic method invocation.
5641
                         */
5642
                        @HashCodeAndEqualsPlugin.Enhance
5643
                        class ForDynamicInvocation implements Dispatcher {
5644

5645
                            /**
5646
                             * The bootstrap method.
5647
                             */
5648
                            private final MethodDescription.InDefinedShape bootstrapMethod;
5649

5650
                            /**
5651
                             * The delegation method.
5652
                             */
5653
                            private final MethodDescription.InDefinedShape delegate;
5654

5655
                            /**
5656
                             * A resolver for supplying arguments to the bootstrap method.
5657
                             */
5658
                            private final BootstrapArgumentResolver resolver;
5659

5660
                            /**
5661
                             * Creates a dispatcher for a dynamic method invocation.
5662
                             *
5663
                             * @param bootstrapMethod The bootstrap method.
5664
                             * @param delegate        The delegation method.
5665
                             * @param resolver        A resolver for supplying arguments to the bootstrap method.
5666
                             */
5667
                            protected ForDynamicInvocation(MethodDescription.InDefinedShape bootstrapMethod,
5668
                                                           MethodDescription.InDefinedShape delegate,
UNCOV
5669
                                                           BootstrapArgumentResolver resolver) {
×
UNCOV
5670
                                this.bootstrapMethod = bootstrapMethod;
×
UNCOV
5671
                                this.delegate = delegate;
×
UNCOV
5672
                                this.resolver = resolver;
×
UNCOV
5673
                            }
×
5674

5675
                            /**
5676
                             * Creates a dispatcher factory for a dynamic method invocation.
5677
                             *
5678
                             * @param bootstrapMethod The bootstrap method.
5679
                             * @param resolverFactory A resolver for supplying arguments to the bootstrap method.
5680
                             * @return An appropriate dispatcher factory.
5681
                             */
5682
                            protected static Dispatcher.Factory of(MethodDescription.InDefinedShape bootstrapMethod,
5683
                                                                   BootstrapArgumentResolver.Factory resolverFactory) {
5684
                                if (!bootstrapMethod.isInvokeBootstrap()) {
×
5685
                                    throw new IllegalStateException("Not a bootstrap method: " + bootstrapMethod);
×
5686
                                }
5687
                                return new ForDynamicInvocation.Factory(bootstrapMethod, resolverFactory);
×
5688
                            }
5689

5690
                            /**
5691
                             * {@inheritDoc}
5692
                             */
5693
                            public Dispatcher.Resolved resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
UNCOV
5694
                                return new ForDynamicInvocation.Resolved(bootstrapMethod, delegate, resolver.resolve(instrumentedType, instrumentedMethod));
×
5695
                            }
5696

5697
                            /**
5698
                             * A resolved dispatcher for a dynamically bound method invocation.
5699
                             */
5700
                            @HashCodeAndEqualsPlugin.Enhance
5701
                            protected static class Resolved implements Dispatcher.Resolved {
5702

5703
                                /**
5704
                                 * The bootstrap method.
5705
                                 */
5706
                                private final MethodDescription.InDefinedShape bootstrapMethod;
5707

5708
                                /**
5709
                                 * The delegation target.
5710
                                 */
5711
                                private final MethodDescription.InDefinedShape delegate;
5712

5713
                                /**
5714
                                 * The bootstrap argument resolver to use.
5715
                                 */
5716
                                private final BootstrapArgumentResolver.Resolved resolver;
5717

5718
                                /**
5719
                                 * Creates a resolved dispatcher of a dynamic method dispatcher.
5720
                                 *
5721
                                 * @param bootstrapMethod The bootstrap method.
5722
                                 * @param delegate        The delegation target.
5723
                                 * @param resolver        The bootstrap argument resolver to use.
5724
                                 */
5725
                                protected Resolved(MethodDescription.InDefinedShape bootstrapMethod,
5726
                                                   MethodDescription.InDefinedShape delegate,
UNCOV
5727
                                                   BootstrapArgumentResolver.Resolved resolver) {
×
UNCOV
5728
                                    this.bootstrapMethod = bootstrapMethod;
×
UNCOV
5729
                                    this.delegate = delegate;
×
UNCOV
5730
                                    this.resolver = resolver;
×
UNCOV
5731
                                }
×
5732

5733
                                /**
5734
                                 * {@inheritDoc}
5735
                                 */
5736
                                public StackManipulation initialize() {
UNCOV
5737
                                    return StackManipulation.Trivial.INSTANCE;
×
5738
                                }
5739

5740
                                /**
5741
                                 * {@inheritDoc}
5742
                                 */
5743
                                public StackManipulation apply(TypeDescription receiver, ByteCodeElement.Member original, JavaConstant.MethodHandle methodHandle) {
UNCOV
5744
                                    List<JavaConstant> constants = resolver.make(receiver, original, methodHandle);
×
UNCOV
5745
                                    if (!bootstrapMethod.isInvokeBootstrap(TypeList.Explicit.of(constants))) {
×
UNCOV
5746
                                        throw new IllegalArgumentException(bootstrapMethod + " is not accepting advice bootstrap arguments: " + constants);
×
5747
                                    }
UNCOV
5748
                                    return MethodInvocation.invoke(bootstrapMethod).dynamic(delegate.getInternalName(),
×
UNCOV
5749
                                            delegate.getReturnType().asErasure(),
×
UNCOV
5750
                                            delegate.getParameters().asTypeList().asErasures(),
×
5751
                                            constants);
5752
                                }
5753
                            }
5754

5755
                            /**
5756
                             * A factory for a dynamic method invocation of the dispatcher method or constructor.
5757
                             */
5758
                            @HashCodeAndEqualsPlugin.Enhance
5759
                            protected static class Factory implements Dispatcher.Factory {
5760

5761
                                /**
5762
                                 * The bootstrap method.
5763
                                 */
5764
                                private final MethodDescription.InDefinedShape bootstrapMethod;
5765

5766
                                /**
5767
                                 * A factory for a bootstrap argument resolver.
5768
                                 */
5769
                                private final BootstrapArgumentResolver.Factory resolverFactory;
5770

5771
                                /**
5772
                                 * Creates a new factory for a dispatcher using a dynamic method invocation.
5773
                                 *
5774
                                 * @param bootstrapMethod The bootstrap method.
5775
                                 * @param resolverFactory A factory for a bootstrap argument resolver.
5776
                                 */
5777
                                protected Factory(MethodDescription.InDefinedShape bootstrapMethod,
UNCOV
5778
                                                  BootstrapArgumentResolver.Factory resolverFactory) {
×
UNCOV
5779
                                    this.bootstrapMethod = bootstrapMethod;
×
UNCOV
5780
                                    this.resolverFactory = resolverFactory;
×
UNCOV
5781
                                }
×
5782

5783
                                /**
5784
                                 * {@inheritDoc}
5785
                                 */
5786
                                public Dispatcher make(MethodDescription.InDefinedShape delegate) {
UNCOV
5787
                                    return new ForDynamicInvocation(bootstrapMethod, delegate, resolverFactory.make(delegate));
×
5788
                                }
5789
                            }
5790
                        }
5791
                    }
5792

5793
                    /**
5794
                     * A resolver for supplying arguments to a bootstrap method which is binding the delegation method's invocation.
5795
                     */
5796
                    public interface BootstrapArgumentResolver {
5797

5798
                        /**
5799
                         * Resolves this resolver for a given instrumented type and method.
5800
                         *
5801
                         * @param instrumentedType   The instrumented type.
5802
                         * @param instrumentedMethod The instrumented method.
5803
                         * @return A resolved version of this argument resolver.
5804
                         */
5805
                        BootstrapArgumentResolver.Resolved resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod);
5806

5807
                        /**
5808
                         * A resolved version of a bootstrap argument handler.
5809
                         */
5810
                        interface Resolved {
5811

5812
                            /**
5813
                             * Returns the constant values to supply to the bootstrap method.
5814
                             *
5815
                             * @param receiver     The type upon which the substituted element is applied.
5816
                             * @param original     The substituted element.
5817
                             * @param methodHandle A method handle that represents the substituted element.
5818
                             * @return A list of constant values to supply to the bootstrap method.
5819
                             */
5820
                            List<JavaConstant> make(TypeDescription receiver, ByteCodeElement.Member original, JavaConstant.MethodHandle methodHandle);
5821
                        }
5822

5823
                        /**
5824
                         * A factory for a bootstrap argument resolver.
5825
                         */
5826
                        interface Factory {
5827

5828
                            /**
5829
                             * Creates a bootstrap argument resolver for a given delegation method.
5830
                             *
5831
                             * @param delegate The method or constructor to which to delegate.
5832
                             * @return An appropriate bootstrap argument resolver.
5833
                             */
5834
                            BootstrapArgumentResolver make(MethodDescription.InDefinedShape delegate);
5835
                        }
5836

5837
                        /**
5838
                         * An implementation that supplies a default set of arguments to a bootstrap method. The arguments are:
5839
                         * <ul>
5840
                         * <li>A {@code java.lang.invoke.MethodHandles.Lookup} representing the source method.</li>
5841
                         * <li>A {@link String} representing the target's internal name.</li>
5842
                         * <li>A {@code java.lang.invoke.MethodType} representing the type that is requested for binding.</li>
5843
                         * <li>A {@link String} representation of the delegate's binary class name.</li>
5844
                         * <li>A {@link Class} representing the receiver type of the substituted element.</li>
5845
                         * <li>A {@link String} representing the internal name of the substituted element.</li>
5846
                         * <li>A {@code java.lang.invoke.MethodHandle} to the substituted element.</li>
5847
                         * <li>A {@link Class} describing the instrumented type.</li>
5848
                         * <li>A {@link String} representing the instrumented method or constructor.</li>
5849
                         * </ul>
5850
                         */
5851
                        @HashCodeAndEqualsPlugin.Enhance
5852
                        class ForDefaultValues implements BootstrapArgumentResolver {
5853

5854
                            /**
5855
                             * The delegation target.
5856
                             */
5857
                            private final MethodDescription.InDefinedShape delegate;
5858

5859
                            /**
5860
                             * Creates a default bootstrap argument resolver.
5861
                             *
5862
                             * @param delegate The delegation target.
5863
                             */
UNCOV
5864
                            protected ForDefaultValues(MethodDescription.InDefinedShape delegate) {
×
UNCOV
5865
                                this.delegate = delegate;
×
UNCOV
5866
                            }
×
5867

5868
                            /**
5869
                             * {@inheritDoc}
5870
                             */
5871
                            public BootstrapArgumentResolver.Resolved resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
UNCOV
5872
                                return new Resolved(delegate, instrumentedType, instrumentedMethod);
×
5873
                            }
5874

5875
                            /**
5876
                             * A resolved default bootstrap argument resolver.
5877
                             */
5878
                            @HashCodeAndEqualsPlugin.Enhance
5879
                            protected static class Resolved implements BootstrapArgumentResolver.Resolved {
5880

5881
                                /**
5882
                                 * The delegation target.
5883
                                 */
5884
                                private final MethodDescription.InDefinedShape delegate;
5885

5886
                                /**
5887
                                 * The instrumented type.
5888
                                 */
5889
                                private final TypeDescription instrumentedType;
5890

5891
                                /**
5892
                                 * The instrumented method.
5893
                                 */
5894
                                private final MethodDescription instrumentedMethod;
5895

5896
                                /**
5897
                                 * Creates a resolved version of a bootstrap argument resolver.
5898
                                 *
5899
                                 * @param delegate           The delegation target.
5900
                                 * @param instrumentedType   The instrumented type.
5901
                                 * @param instrumentedMethod The instrumented method.
5902
                                 */
UNCOV
5903
                                protected Resolved(MethodDescription.InDefinedShape delegate, TypeDescription instrumentedType, MethodDescription instrumentedMethod) {
×
UNCOV
5904
                                    this.delegate = delegate;
×
UNCOV
5905
                                    this.instrumentedType = instrumentedType;
×
UNCOV
5906
                                    this.instrumentedMethod = instrumentedMethod;
×
UNCOV
5907
                                }
×
5908

5909
                                /**
5910
                                 * {@inheritDoc}
5911
                                 */
5912
                                public List<JavaConstant> make(TypeDescription receiver, ByteCodeElement.Member original, JavaConstant.MethodHandle methodHandle) {
UNCOV
5913
                                    if (instrumentedMethod.isTypeInitializer()) {
×
UNCOV
5914
                                        return Arrays.asList(JavaConstant.Simple.ofLoaded(delegate.getDeclaringType().getName()),
×
UNCOV
5915
                                                JavaConstant.Simple.of(receiver),
×
UNCOV
5916
                                                JavaConstant.Simple.ofLoaded(original.getInternalName()),
×
5917
                                                methodHandle,
UNCOV
5918
                                                JavaConstant.Simple.of(instrumentedType),
×
UNCOV
5919
                                                JavaConstant.Simple.ofLoaded(instrumentedMethod.getInternalName()));
×
5920
                                    } else {
5921
                                        return Arrays.asList(JavaConstant.Simple.ofLoaded(delegate.getDeclaringType().getName()),
×
5922
                                                JavaConstant.Simple.of(receiver),
×
5923
                                                JavaConstant.Simple.ofLoaded(original.getInternalName()),
×
5924
                                                methodHandle,
UNCOV
5925
                                                JavaConstant.Simple.of(instrumentedType),
×
UNCOV
5926
                                                JavaConstant.Simple.ofLoaded(instrumentedMethod.getInternalName()),
×
UNCOV
5927
                                                JavaConstant.MethodHandle.of(instrumentedMethod.asDefined()));
×
5928
                                    }
5929
                                }
5930
                            }
5931

5932
                            /**
5933
                             * A factory for creating a default bootstrap argument resolver.
5934
                             */
UNCOV
5935
                            public enum Factory implements BootstrapArgumentResolver.Factory {
×
5936

5937
                                /**
5938
                                 * The singleton instance.
5939
                                 */
UNCOV
5940
                                INSTANCE;
×
5941

5942
                                /**
5943
                                 * {@inheritDoc}
5944
                                 */
5945
                                public BootstrapArgumentResolver make(MethodDescription.InDefinedShape delegate) {
UNCOV
5946
                                    return new ForDefaultValues(delegate);
×
5947
                                }
5948
                            }
5949
                        }
5950
                    }
5951

5952
                    /**
5953
                     * A factory for a {@link ForDelegation} which allows for a custom configuration.
5954
                     */
5955
                    public static class WithCustomMapping {
5956

5957
                        /**
5958
                         * The dispatcher factory to use.
5959
                         */
5960
                        private final Dispatcher.Factory dispatcherFactory;
5961

5962
                        /**
5963
                         * A mapping of offset mapping factories by their respective annotation type.
5964
                         */
5965
                        private final Map<Class<? extends Annotation>, OffsetMapping.Factory<?>> offsetMappings;
5966

5967
                        /**
5968
                         * Creates a factory for a {@link ForDelegation} with a custom value.
5969
                         *
5970
                         * @param dispatcherFactory The dispatcher factory to use.
5971
                         * @param offsetMappings    A mapping of offset mapping factories by their respective annotation type.
5972
                         */
5973
                        protected WithCustomMapping(Dispatcher.Factory dispatcherFactory, Map<Class<? extends Annotation>, OffsetMapping.Factory<?>> offsetMappings) {
1✔
5974
                            this.dispatcherFactory = dispatcherFactory;
1✔
5975
                            this.offsetMappings = offsetMappings;
1✔
5976
                        }
1✔
5977

5978
                        /**
5979
                         * Binds the supplied annotation to a type constant of the supplied value. Constants can be strings, method handles, method types
5980
                         * and any primitive or the value {@code null}.
5981
                         *
5982
                         * @param type  The type of the annotation being bound.
5983
                         * @param value The value to bind to the annotation or {@code null} to bind the parameter type's default value.
5984
                         * @param <T>   The annotation type.
5985
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
5986
                         */
5987
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, @MaybeNull Object value) {
UNCOV
5988
                            return bind(OffsetMapping.ForStackManipulation.of(type, value));
×
5989
                        }
5990

5991
                        /**
5992
                         * Binds the supplied annotation to the value of the supplied field. The field must be visible by the
5993
                         * instrumented type and must be declared by a super type of the instrumented field.
5994
                         *
5995
                         * @param type  The type of the annotation being bound.
5996
                         * @param field The field to bind to this annotation.
5997
                         * @param <T>   The annotation type.
5998
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
5999
                         */
6000
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, Field field) {
6001
                            return bind(type, new FieldDescription.ForLoadedField(field));
1✔
6002
                        }
6003

6004
                        /**
6005
                         * Binds the supplied annotation to the value of the supplied field. The field must be visible by the
6006
                         * instrumented type and must be declared by a super type of the instrumented field. The binding is defined
6007
                         * as read-only and applied static typing.
6008
                         *
6009
                         * @param type             The type of the annotation being bound.
6010
                         * @param fieldDescription The field to bind to this annotation.
6011
                         * @param <T>              The annotation type.
6012
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
6013
                         */
6014
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, FieldDescription fieldDescription) {
6015
                            return bind(new OffsetMapping.ForField.Resolved.Factory<T>(type, fieldDescription));
1✔
6016
                        }
6017

6018
                        /**
6019
                         * Binds the supplied annotation to the supplied type constant.
6020
                         *
6021
                         * @param type  The type of the annotation being bound.
6022
                         * @param value The type constant to bind.
6023
                         * @param <T>   The annotation type.
6024
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
6025
                         */
6026
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, Class<?> value) {
UNCOV
6027
                            return bind(type, TypeDescription.ForLoadedType.of(value));
×
6028
                        }
6029

6030
                        /**
6031
                         * Binds the supplied annotation to the supplied type constant.
6032
                         *
6033
                         * @param type  The type of the annotation being bound.
6034
                         * @param value The type constant to bind.
6035
                         * @param <T>   The annotation type.
6036
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
6037
                         */
6038
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, TypeDescription value) {
UNCOV
6039
                            return bind(new OffsetMapping.ForStackManipulation.Factory<T>(type, ConstantValue.Simple.wrap(value)));
×
6040
                        }
6041

6042
                        /**
6043
                         * Binds the supplied annotation to the supplied enumeration constant.
6044
                         *
6045
                         * @param type  The type of the annotation being bound.
6046
                         * @param value The enumeration constant to bind.
6047
                         * @param <T>   The annotation type.
6048
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
6049
                         */
6050
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, Enum<?> value) {
UNCOV
6051
                            return bind(type, new EnumerationDescription.ForLoadedEnumeration(value));
×
6052
                        }
6053

6054
                        /**
6055
                         * Binds the supplied annotation to the supplied enumeration constant.
6056
                         *
6057
                         * @param type  The type of the annotation being bound.
6058
                         * @param value The enumeration constant to bind.
6059
                         * @param <T>   The annotation type.
6060
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
6061
                         */
6062
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, EnumerationDescription value) {
UNCOV
6063
                            return bind(new OffsetMapping.ForStackManipulation.Factory<T>(type, ConstantValue.Simple.wrap(value)));
×
6064
                        }
6065

6066
                        /**
6067
                         * Binds the supplied annotation to the supplied fixed value.
6068
                         *
6069
                         * @param type  The type of the annotation being bound.
6070
                         * @param value The value to bind to this annotation.
6071
                         * @param <T>   The annotation type.
6072
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
6073
                         */
6074
                        @SuppressWarnings("unchecked")
6075
                        public <T extends Annotation> WithCustomMapping bindSerialized(Class<T> type, Serializable value) {
6076
                            return bindSerialized(type, value, (Class<Serializable>) value.getClass());
1✔
6077
                        }
6078

6079
                        /**
6080
                         * Binds the supplied annotation to the supplied fixed value.
6081
                         *
6082
                         * @param type       The type of the annotation being bound.
6083
                         * @param value      The value to bind to this annotation.
6084
                         * @param targetType The type of {@code value} as which the instance should be treated.
6085
                         * @param <T>        The annotation type.
6086
                         * @param <S>        The type of the serialized instance.
6087
                         * @return A new builder for a delegate that considers the supplied annotation type during binding.
6088
                         */
6089
                        public <T extends Annotation, S extends Serializable> WithCustomMapping bindSerialized(Class<T> type, S value, Class<? super S> targetType) {
6090
                            return bind(OffsetMapping.ForStackManipulation.OfSerializedConstant.of(type, value, targetType));
1✔
6091
                        }
6092

6093
                        /**
6094
                         * Binds the supplied annotation to the annotation's property of the specified name.
6095
                         *
6096
                         * @param type     The type of the annotation being bound.
6097
                         * @param property The name of the annotation property to be bound.
6098
                         * @param <T>      The annotation type.
6099
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6100
                         */
6101
                        public <T extends Annotation> WithCustomMapping bindProperty(Class<T> type, String property) {
UNCOV
6102
                            return bind(OffsetMapping.ForStackManipulation.OfAnnotationProperty.of(type, property));
×
6103
                        }
6104

6105
                        /**
6106
                         * Binds the supplied annotation to the given Java constant.
6107
                         *
6108
                         * @param type     The type of the annotation being bound.
6109
                         * @param constant The constant value that is bound.
6110
                         * @param <T>      The annotation type.
6111
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6112
                         */
6113
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, ConstantValue constant) {
UNCOV
6114
                            return bind(new OffsetMapping.ForStackManipulation.Factory<T>(type, constant.toStackManipulation(), constant.getTypeDescription().asGenericType()));
×
6115
                        }
6116

6117
                        /**
6118
                         * Binds the supplied annotation to the annotation's property of the specified name.
6119
                         *
6120
                         * @param type              The type of the annotation being bound.
6121
                         * @param stackManipulation The stack manipulation loading the bound value.
6122
                         * @param targetType        The type of the loaded value.
6123
                         * @param <T>               The annotation type.
6124
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6125
                         */
6126
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, StackManipulation stackManipulation, java.lang.reflect.Type targetType) {
UNCOV
6127
                            return bind(type, stackManipulation, TypeDefinition.Sort.describe(targetType));
×
6128
                        }
6129

6130
                        /**
6131
                         * Binds the supplied annotation to the annotation's property of the specified name.
6132
                         *
6133
                         * @param type              The type of the annotation being bound.
6134
                         * @param stackManipulation The stack manipulation loading the bound value.
6135
                         * @param targetType        The type of the loaded value.
6136
                         * @param <T>               The annotation type.
6137
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6138
                         */
6139
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, StackManipulation stackManipulation, TypeDescription.Generic targetType) {
UNCOV
6140
                            return bind(new OffsetMapping.ForStackManipulation.Factory<T>(type, stackManipulation, targetType));
×
6141
                        }
6142

6143
                        /**
6144
                         * Binds the supplied annotation as a lambda expression via the JVM's lambda metafactory.
6145
                         *
6146
                         * @param type                The type of the annotation being bound.
6147
                         * @param constructor         The constructor being bound as the lambda expression's implementation.
6148
                         * @param functionalInterface The functional interface that represents the lambda expression.
6149
                         * @param <T>                 The annotation type.
6150
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6151
                         */
6152
                        public <T extends Annotation> WithCustomMapping bindLambda(Class<T> type, Constructor<?> constructor, Class<?> functionalInterface) {
UNCOV
6153
                            return bindLambda(type, new MethodDescription.ForLoadedConstructor(constructor), TypeDescription.ForLoadedType.of(functionalInterface));
×
6154
                        }
6155

6156
                        /**
6157
                         * Binds the supplied annotation as a lambda expression via the JVM's lambda metafactory.
6158
                         *
6159
                         * @param type                The type of the annotation being bound.
6160
                         * @param constructor         The constructor being bound as the lambda expression's implementation.
6161
                         * @param functionalInterface The functional interface that represents the lambda expression.
6162
                         * @param methodGraphCompiler The method graph compiler that resolves the functional method of the function interface.
6163
                         * @param <T>                 The annotation type.
6164
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6165
                         */
6166
                        public <T extends Annotation> WithCustomMapping bindLambda(Class<T> type, Constructor<?> constructor, Class<?> functionalInterface, MethodGraph.Compiler methodGraphCompiler) {
UNCOV
6167
                            return bindLambda(type, new MethodDescription.ForLoadedConstructor(constructor), TypeDescription.ForLoadedType.of(functionalInterface), methodGraphCompiler);
×
6168
                        }
6169

6170
                        /**
6171
                         * Binds the supplied annotation as a lambda expression via the JVM's lambda metafactory.
6172
                         *
6173
                         * @param type                The type of the annotation being bound.
6174
                         * @param method              The method being bound as the lambda expression's implementation.
6175
                         * @param functionalInterface The functional interface that represents the lambda expression.
6176
                         * @param <T>                 The annotation type.
6177
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6178
                         */
6179
                        public <T extends Annotation> WithCustomMapping bindLambda(Class<T> type, Method method, Class<?> functionalInterface) {
UNCOV
6180
                            return bindLambda(type, new MethodDescription.ForLoadedMethod(method), TypeDescription.ForLoadedType.of(functionalInterface));
×
6181
                        }
6182

6183
                        /**
6184
                         * Binds the supplied annotation as a lambda expression via the JVM's lambda metafactory.
6185
                         *
6186
                         * @param type                The type of the annotation being bound.
6187
                         * @param method              The method being bound as the lambda expression's implementation.
6188
                         * @param functionalInterface The functional interface that represents the lambda expression.
6189
                         * @param methodGraphCompiler The method graph compiler that resolves the functional method of the function interface.
6190
                         * @param <T>                 The annotation type.
6191
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6192
                         */
6193
                        public <T extends Annotation> WithCustomMapping bindLambda(Class<T> type, Method method, Class<?> functionalInterface, MethodGraph.Compiler methodGraphCompiler) {
UNCOV
6194
                            return bindLambda(type, new MethodDescription.ForLoadedMethod(method), TypeDescription.ForLoadedType.of(functionalInterface), methodGraphCompiler);
×
6195
                        }
6196

6197
                        /**
6198
                         * Binds the supplied annotation as a lambda expression via the JVM's lambda metafactory.
6199
                         *
6200
                         * @param type                The type of the annotation being bound.
6201
                         * @param methodDescription   The method or constructor being bound as the lambda expression's implementation.
6202
                         * @param functionalInterface The functional interface that represents the lambda expression.
6203
                         * @param <T>                 The annotation type.
6204
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6205
                         */
6206
                        public <T extends Annotation> WithCustomMapping bindLambda(Class<T> type, MethodDescription.InDefinedShape methodDescription, TypeDescription functionalInterface) {
UNCOV
6207
                            return bindLambda(type, methodDescription, functionalInterface, MethodGraph.Compiler.DEFAULT);
×
6208
                        }
6209

6210
                        /**
6211
                         * Binds the supplied annotation as a lambda expression via the JVM's lambda metafactory.
6212
                         *
6213
                         * @param type                The type of the annotation being bound.
6214
                         * @param methodDescription   The method or constuctor being bound as the lambda expression's implementation.
6215
                         * @param functionalInterface The functional interface that represents the lambda expression.
6216
                         * @param methodGraphCompiler The method graph compiler that resolves the functional method of the function interface.
6217
                         * @param <T>                 The annotation type.
6218
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6219
                         */
6220
                        public <T extends Annotation> WithCustomMapping bindLambda(Class<T> type,
6221
                                                                                   MethodDescription.InDefinedShape methodDescription,
6222
                                                                                   TypeDescription functionalInterface,
6223
                                                                                   MethodGraph.Compiler methodGraphCompiler) {
6224
                            if (!functionalInterface.isInterface()) {
×
UNCOV
6225
                                throw new IllegalArgumentException(functionalInterface + " is not an interface type");
×
6226
                            }
UNCOV
6227
                            MethodList<?> methods = methodGraphCompiler.compile((TypeDefinition) functionalInterface).listNodes().asMethodList().filter(isAbstract());
×
UNCOV
6228
                            if (methods.size() != 1) {
×
UNCOV
6229
                                throw new IllegalArgumentException(functionalInterface + " does not define exactly one abstract method: " + methods);
×
6230
                            }
UNCOV
6231
                            return bindDynamic(type, new MethodDescription.Latent(new TypeDescription.Latent("java.lang.invoke.LambdaMetafactory",
×
6232
                                            Opcodes.ACC_PUBLIC,
UNCOV
6233
                                            TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(Object.class)),
×
6234
                                            "metafactory",
6235
                                            Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC,
UNCOV
6236
                                            Collections.<TypeVariableToken>emptyList(),
×
6237
                                            JavaType.CALL_SITE.getTypeStub().asGenericType(),
×
UNCOV
6238
                                            Arrays.asList(
×
UNCOV
6239
                                                    new ParameterDescription.Token(JavaType.METHOD_HANDLES_LOOKUP.getTypeStub().asGenericType()),
×
UNCOV
6240
                                                    new ParameterDescription.Token(TypeDescription.ForLoadedType.of(String.class).asGenericType()),
×
UNCOV
6241
                                                    new ParameterDescription.Token(JavaType.METHOD_TYPE.getTypeStub().asGenericType()),
×
UNCOV
6242
                                                    new ParameterDescription.Token(JavaType.METHOD_TYPE.getTypeStub().asGenericType()),
×
UNCOV
6243
                                                    new ParameterDescription.Token(JavaType.METHOD_HANDLE.getTypeStub().asGenericType()),
×
UNCOV
6244
                                                    new ParameterDescription.Token(JavaType.METHOD_TYPE.getTypeStub().asGenericType())),
×
UNCOV
6245
                                            Collections.<TypeDescription.Generic>emptyList(),
×
UNCOV
6246
                                            Collections.<AnnotationDescription>emptyList(),
×
6247
                                            AnnotationValue.UNDEFINED,
6248
                                            TypeDescription.Generic.UNDEFINED),
UNCOV
6249
                                    JavaConstant.MethodType.ofSignature(methods.asDefined().getOnly()),
×
UNCOV
6250
                                    JavaConstant.MethodHandle.of(methodDescription),
×
6251
                                    JavaConstant.MethodType.ofSignature(methods.asDefined().getOnly()));
×
6252
                        }
6253

6254
                        /**
6255
                         * Binds the supplied annotation to a dynamically bootstrapped value.
6256
                         *
6257
                         * @param type            The type of the annotation being bound.
6258
                         * @param bootstrapMethod The bootstrap method returning the call site.
6259
                         * @param constant        The arguments supplied to the bootstrap method.
6260
                         * @param <T>             The annotation type.
6261
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6262
                         */
6263
                        public <T extends Annotation> WithCustomMapping bindDynamic(Class<T> type, Method bootstrapMethod, Object... constant) {
6264
                            return bindDynamic(type, bootstrapMethod, Arrays.asList(constant));
×
6265
                        }
6266

6267
                        /**
6268
                         * Binds the supplied annotation to a dynamically bootstrapped value.
6269
                         *
6270
                         * @param type            The type of the annotation being bound.
6271
                         * @param bootstrapMethod The bootstrap method returning the call site.
6272
                         * @param constants       The arguments supplied to the bootstrap method.
6273
                         * @param <T>             The annotation type.
6274
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6275
                         */
6276
                        public <T extends Annotation> WithCustomMapping bindDynamic(Class<T> type, Method bootstrapMethod, List<?> constants) {
UNCOV
6277
                            return bindDynamic(type, new MethodDescription.ForLoadedMethod(bootstrapMethod), constants);
×
6278
                        }
6279

6280
                        /**
6281
                         * Binds the supplied annotation to a dynamically bootstrapped value.
6282
                         *
6283
                         * @param type            The type of the annotation being bound.
6284
                         * @param bootstrapMethod The bootstrap constructor returning the call site.
6285
                         * @param constant        The arguments supplied to the bootstrap method.
6286
                         * @param <T>             The annotation type.
6287
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6288
                         */
6289
                        public <T extends Annotation> WithCustomMapping bindDynamic(Class<T> type, Constructor<?> bootstrapMethod, Object... constant) {
6290
                            return bindDynamic(type, bootstrapMethod, Arrays.asList(constant));
×
6291
                        }
6292

6293
                        /**
6294
                         * Binds the supplied annotation to a dynamically bootstrapped value.
6295
                         *
6296
                         * @param type            The type of the annotation being bound.
6297
                         * @param bootstrapMethod The bootstrap constructor returning the call site.
6298
                         * @param constants       The arguments supplied to the bootstrap method.
6299
                         * @param <T>             The annotation type.
6300
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6301
                         */
6302
                        public <T extends Annotation> WithCustomMapping bindDynamic(Class<T> type, Constructor<?> bootstrapMethod, List<?> constants) {
6303
                            return bindDynamic(type, new MethodDescription.ForLoadedConstructor(bootstrapMethod), constants);
×
6304
                        }
6305

6306
                        /**
6307
                         * Binds the supplied annotation to a dynamically bootstrapped value.
6308
                         *
6309
                         * @param type            The type of the annotation being bound.
6310
                         * @param bootstrapMethod The bootstrap method or constructor returning the call site.
6311
                         * @param constant        The arguments supplied to the bootstrap method.
6312
                         * @param <T>             The annotation type.
6313
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6314
                         */
6315
                        public <T extends Annotation> WithCustomMapping bindDynamic(Class<T> type, MethodDescription.InDefinedShape bootstrapMethod, Object... constant) {
UNCOV
6316
                            return bindDynamic(type, bootstrapMethod, Arrays.asList(constant));
×
6317
                        }
6318

6319
                        /**
6320
                         * Binds the supplied annotation to a dynamically bootstrapped value.
6321
                         *
6322
                         * @param type            The type of the annotation being bound.
6323
                         * @param bootstrapMethod The bootstrap method or constructor returning the call site.
6324
                         * @param constants       The arguments supplied to the bootstrap method.
6325
                         * @param <T>             The annotation type.
6326
                         * @return A new builder for a delegate that considers the supplied annotation during binding.
6327
                         */
6328
                        public <T extends Annotation> WithCustomMapping bindDynamic(Class<T> type, MethodDescription.InDefinedShape bootstrapMethod, List<?> constants) {
UNCOV
6329
                            List<JavaConstant> arguments = JavaConstant.Simple.wrap(constants);
×
UNCOV
6330
                            if (!bootstrapMethod.isInvokeBootstrap(TypeList.Explicit.of(arguments))) {
×
UNCOV
6331
                                throw new IllegalArgumentException("Not a valid bootstrap method " + bootstrapMethod + " for " + arguments);
×
6332
                            }
UNCOV
6333
                            return bind(new OffsetMapping.ForStackManipulation.OfDynamicInvocation<T>(type, bootstrapMethod, arguments));
×
6334
                        }
6335

6336
                        /**
6337
                         * Binds the supplied annotation to the annotation's property of the specified name.
6338
                         *
6339
                         * @param type          The type of the annotation being bound.
6340
                         * @param offsetMapping The offset mapping being bound.
6341
                         * @param <T>           The annotation type.
6342
                         * @return A new builder for a delegation that considers the supplied annotation during binding.
6343
                         */
6344
                        public <T extends Annotation> WithCustomMapping bind(Class<T> type, OffsetMapping offsetMapping) {
UNCOV
6345
                            return bind(new OffsetMapping.Factory.Simple<T>(type, offsetMapping));
×
6346
                        }
6347

6348
                        /**
6349
                         * Binds an annotation to a dynamically computed value. Whenever the {@link ForDelegation} target discovers the given annotation on
6350
                         * a parameter of an advice method, the dynamic value is asked to provide a value that is then assigned to the parameter in question.
6351
                         *
6352
                         * @param offsetMapping The dynamic value that is computed for binding the parameter to a value.
6353
                         * @return A new builder for a delegation that considers the supplied annotation type during binding.
6354
                         */
6355
                        public WithCustomMapping bind(OffsetMapping.Factory<?> offsetMapping) {
6356
                            Map<Class<? extends Annotation>, OffsetMapping.Factory<?>> offsetMappings = new LinkedHashMap<Class<? extends Annotation>, OffsetMapping.Factory<?>>(this.offsetMappings);
1✔
6357
                            if (!offsetMapping.getAnnotationType().isAnnotation()) {
1✔
UNCOV
6358
                                throw new IllegalArgumentException("Not an annotation type: " + offsetMapping.getAnnotationType());
×
6359
                            } else if (offsetMappings.put(offsetMapping.getAnnotationType(), offsetMapping) != null) {
1✔
6360
                                throw new IllegalArgumentException("Annotation type already mapped: " + offsetMapping.getAnnotationType());
×
6361
                            }
6362
                            return new WithCustomMapping(dispatcherFactory, offsetMappings);
1✔
6363
                        }
6364

6365
                        /**
6366
                         * Defines the supplied constructor as a dynamic invocation bootstrap target for delegating advice methods. The bootstrap
6367
                         * method arguments are:
6368
                         * <ul>
6369
                         * <li>A {@code java.lang.invoke.MethodHandles.Lookup} representing the source method.</li>
6370
                         * <li>A {@link String} representing the constructor's internal name {@code <init>}.</li>
6371
                         * <li>A {@code java.lang.invoke.MethodType} representing the type that is requested for binding.</li>
6372
                         * <li>A {@link String} representation of the delegate's binary class name.</li>
6373
                         * <li>A {@link Class} representing the receiver type of the substituted element.</li>
6374
                         * <li>A {@link String} representing the internal name of the substituted element.</li>
6375
                         * <li>A {@code java.lang.invoke.MethodHandle} to the substituted element.</li>
6376
                         * <li>A {@link Class} describing the instrumented type.</li>
6377
                         * <li>A {@link String} representing the instrumented method or constructor.</li>
6378
                         * </ul>
6379
                         *
6380
                         * @param constructor The bootstrap constructor.
6381
                         * @return A new builder for a delegation within a member substitution that uses the supplied constructor for bootstrapping.
6382
                         */
6383
                        public WithCustomMapping bootstrap(Constructor<?> constructor) {
UNCOV
6384
                            return bootstrap(new MethodDescription.ForLoadedConstructor(constructor));
×
6385
                        }
6386

6387
                        /**
6388
                         * Defines the supplied constructor as a dynamic invocation bootstrap target for delegating advice methods.
6389
                         *
6390
                         * @param constructor     The bootstrap method or constructor.
6391
                         * @param resolverFactory A factory for resolving the arguments to the bootstrap method.
6392
                         * @return A new builder for a delegation within a member substitution that uses the supplied constructor for bootstrapping.
6393
                         */
6394
                        public WithCustomMapping bootstrap(Constructor<?> constructor, BootstrapArgumentResolver.Factory resolverFactory) {
UNCOV
6395
                            return bootstrap(new MethodDescription.ForLoadedConstructor(constructor), resolverFactory);
×
6396
                        }
6397

6398
                        /**
6399
                         * Defines the supplied method as a dynamic invocation bootstrap target for delegating advice methods. The bootstrap
6400
                         * method arguments are:
6401
                         * <ul>
6402
                         * <li>A {@code java.lang.invoke.MethodHandles.Lookup} representing the source method.</li>
6403
                         * <li>A {@link String} representing the method's name.</li>
6404
                         * <li>A {@code java.lang.invoke.MethodType} representing the type that is requested for binding.</li>
6405
                         * <li>A {@link String} representation of the delegate's binary class name.</li>
6406
                         * <li>A {@link Class} representing the receiver type of the substituted element.</li>
6407
                         * <li>A {@link String} representing the internal name of the substituted element.</li>
6408
                         * <li>A {@code java.lang.invoke.MethodHandle} to the substituted element.</li>
6409
                         * <li>A {@link Class} describing the instrumented type.</li>
6410
                         * <li>A {@link String} representing the instrumented method or constructor.</li>
6411
                         * </ul>
6412
                         *
6413
                         * @param method The bootstrap method.
6414
                         * @return A new builder for a delegation within a member substitution that uses the supplied method for bootstrapping.
6415
                         */
6416
                        public WithCustomMapping bootstrap(Method method) {
6417
                            return bootstrap(new MethodDescription.ForLoadedMethod(method));
×
6418
                        }
6419

6420
                        /**
6421
                         * Defines the supplied method as a dynamic invocation bootstrap target for delegating advice methods.
6422
                         *
6423
                         * @param method          The bootstrap method or constructor.
6424
                         * @param resolverFactory A factory for resolving the arguments to the bootstrap method.
6425
                         * @return A new builder for a delegation within a member substitution that uses the supplied method for bootstrapping.
6426
                         */
6427
                        public WithCustomMapping bootstrap(Method method, BootstrapArgumentResolver.Factory resolverFactory) {
UNCOV
6428
                            return bootstrap(new MethodDescription.ForLoadedMethod(method), resolverFactory);
×
6429
                        }
6430

6431
                        /**
6432
                         * Defines the supplied method description as a dynamic invocation bootstrap target for delegating advice methods. The bootstrap
6433
                         * method arguments are:
6434
                         * <ul>
6435
                         * <li>A {@code java.lang.invoke.MethodHandles.Lookup} representing the source method.</li>
6436
                         * <li>A {@link String} representing the target's internal name.</li>
6437
                         * <li>A {@code java.lang.invoke.MethodType} representing the type that is requested for binding.</li>
6438
                         * <li>A {@link String} representation of the delegate's binary class name.</li>
6439
                         * <li>A {@link Class} representing the receiver type of the substituted element.</li>
6440
                         * <li>A {@link String} representing the internal name of the substituted element.</li>
6441
                         * <li>A {@code java.lang.invoke.MethodHandle} to the substituted element.</li>
6442
                         * <li>A {@link Class} describing the instrumented type.</li>
6443
                         * <li>A {@link String} representing the instrumented method or constructor.</li>
6444
                         * <li>A method handle of the instrumented method or constructor, only if the instrumented method is not a type initializer.</li>
6445
                         * </ul>
6446
                         *
6447
                         * @param bootstrap The bootstrap method or constructor.
6448
                         * @return A new builder for a delegation within a member substitution that uses the supplied method or constructor for bootstrapping.
6449
                         */
6450
                        public WithCustomMapping bootstrap(MethodDescription.InDefinedShape bootstrap) {
UNCOV
6451
                            return bootstrap(bootstrap, BootstrapArgumentResolver.ForDefaultValues.Factory.INSTANCE);
×
6452
                        }
6453

6454
                        /**
6455
                         * Defines the supplied method description as a dynamic invocation bootstrap target for delegating advice methods.
6456
                         *
6457
                         * @param bootstrap       The bootstrap method or constructor.
6458
                         * @param resolverFactory A factory for resolving the arguments to the bootstrap method.
6459
                         * @return A new builder for a delegation within a member substitution that uses the supplied method or constructor for bootstrapping.
6460
                         */
6461
                        public WithCustomMapping bootstrap(MethodDescription.InDefinedShape bootstrap, BootstrapArgumentResolver.Factory resolverFactory) {
UNCOV
6462
                            return new WithCustomMapping(Dispatcher.ForDynamicInvocation.of(bootstrap, resolverFactory), offsetMappings);
×
6463
                        }
6464

6465
                        /**
6466
                         * Returns a delegating step factory for the supplied method.
6467
                         *
6468
                         * @param method The method to delegate to.
6469
                         * @return An appropriate step factory.
6470
                         */
6471
                        public Step.Factory to(Method method) {
6472
                            return to(new MethodDescription.ForLoadedMethod(method));
1✔
6473
                        }
6474

6475
                        /**
6476
                         * Returns a delegating step factory for the supplied constructor.
6477
                         *
6478
                         * @param constructor the constructor to delegate to.
6479
                         * @return An appropriate step factory.
6480
                         */
6481
                        public Step.Factory to(Constructor<?> constructor) {
UNCOV
6482
                            return to(new MethodDescription.ForLoadedConstructor(constructor));
×
6483
                        }
6484

6485
                        /**
6486
                         * Returns a delegating step factory for the supplied method description.
6487
                         *
6488
                         * @param methodDescription A description of the method or constructor to delegate to.
6489
                         * @return An appropriate step factory.
6490
                         */
6491
                        public Step.Factory to(MethodDescription.InDefinedShape methodDescription) {
6492
                            return ForDelegation.to(methodDescription, dispatcherFactory, new ArrayList<OffsetMapping.Factory<?>>(offsetMappings.values()));
1✔
6493
                        }
6494
                    }
6495
                }
6496
            }
6497

6498
            /**
6499
             * A factory for creating a substitution chain.
6500
             */
6501
            @HashCodeAndEqualsPlugin.Enhance
6502
            public static class Factory implements Substitution.Factory {
6503

6504
                /**
6505
                 * The assigner to use.
6506
                 */
6507
                private final Assigner assigner;
6508

6509
                /**
6510
                 * The typing of the assignment to use.
6511
                 */
6512
                private final Assigner.Typing typing;
6513

6514
                /**
6515
                 * The substitution steps to apply.
6516
                 */
6517
                private final List<Step.Factory> steps;
6518

6519
                /**
6520
                 * Creates a new factory for a substitution chain.
6521
                 *
6522
                 * @param assigner The assigner to use.
6523
                 * @param typing   The typing of the assignment to use.
6524
                 * @param steps    The substitution steps to apply.
6525
                 */
6526
                protected Factory(Assigner assigner, Assigner.Typing typing, List<Step.Factory> steps) {
1✔
6527
                    this.assigner = assigner;
1✔
6528
                    this.typing = typing;
1✔
6529
                    this.steps = steps;
1✔
6530
                }
1✔
6531

6532
                /**
6533
                 * {@inheritDoc}
6534
                 */
6535
                public Substitution make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
6536
                    if (steps.isEmpty()) {
1✔
6537
                        return Stubbing.INSTANCE;
1✔
6538
                    }
6539
                    List<Step> steps = new ArrayList<Step>(this.steps.size());
1✔
6540
                    for (Step.Factory step : this.steps) {
1✔
6541
                        steps.add(step.make(assigner, typing, instrumentedType, instrumentedMethod));
1✔
6542
                    }
1✔
6543
                    return new Chain(assigner, typing, steps);
1✔
6544
                }
6545

6546
                /**
6547
                 * Appends the supplied steps to the substitution chain.
6548
                 *
6549
                 * @param step The steps to append.
6550
                 * @return A new substitution chain that is equal to this substitution chain but with the supplied steps appended.
6551
                 */
6552
                public Factory executing(Step.Factory... step) {
UNCOV
6553
                    return executing(Arrays.asList(step));
×
6554
                }
6555

6556
                /**
6557
                 * Appends the supplied steps to the substitution chain.
6558
                 *
6559
                 * @param steps The steps to append.
6560
                 * @return A new substitution chain that is equal to this substitution chain but with the supplied steps appended.
6561
                 */
6562
                public Factory executing(List<? extends Step.Factory> steps) {
6563
                    return new Factory(assigner, typing, CompoundList.of(this.steps, steps));
1✔
6564
                }
6565
            }
6566
        }
6567
    }
6568

6569
    /**
6570
     * A replacement combines a {@link Substitution} and a way of choosing if this substitution should be applied for a discovered member.
6571
     */
6572
    protected interface Replacement {
6573

6574
        /**
6575
         * Binds this replacement for a field that was discovered.
6576
         *
6577
         * @param instrumentedType   The instrumented type.
6578
         * @param instrumentedMethod The instrumented method.
6579
         * @param typeDescription    The type on which the field was read.
6580
         * @param fieldDescription   The field that was discovered.
6581
         * @param writeAccess        {@code true} if this field was written to.
6582
         * @return A binding for the discovered field access.
6583
         */
6584
        Binding bind(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypeDescription typeDescription, FieldDescription fieldDescription, boolean writeAccess);
6585

6586
        /**
6587
         * Binds this replacement for a field that was discovered.
6588
         *
6589
         * @param instrumentedType   The instrumented type.FieldDescription
6590
         * @param instrumentedMethod The instrumented method.
6591
         * @param typeDescription    The type on which the method was invoked.
6592
         * @param methodDescription  The method that was discovered.
6593
         * @param invocationType     The invocation type for this method.
6594
         * @return A binding for the discovered method invocation.
6595
         */
6596
        Binding bind(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypeDescription typeDescription, MethodDescription methodDescription, InvocationType invocationType);
6597

6598
        /**
6599
         * A binding for a replacement of a field or method access within another method.
6600
         */
6601
        interface Binding {
6602

6603
            /**
6604
             * Returns {@code true} if this binding is resolved.
6605
             *
6606
             * @return {@code true} if this binding is resolved.
6607
             */
6608
            boolean isBound();
6609

6610
            /**
6611
             * Creates a stack manipulation that represents the substitution. This method can only be called for actually bound bindings.
6612
             *
6613
             * @param parameters        The parameters that are accessible to the substitution target.
6614
             * @param result            The result that is expected from the substitution target or {@code void} if none is expected.
6615
             * @param methodHandle      A method handle that represents the original expression that is being substituted.
6616
             * @param stackManipulation The original byte code expression that is being substituted.
6617
             * @param freeOffset        The first offset that can be used for storing local variables.
6618
             * @return A stack manipulation that represents the replacement.
6619
             */
6620
            StackManipulation make(TypeList.Generic parameters,
6621
                                   TypeDescription.Generic result,
6622
                                   JavaConstant.MethodHandle methodHandle,
6623
                                   StackManipulation stackManipulation,
6624
                                   int freeOffset);
6625

6626
            /**
6627
             * An unresolved binding.
6628
             */
6629
            enum Unresolved implements Binding {
1✔
6630

6631
                /**
6632
                 * The singleton instance.
6633
                 */
6634
                INSTANCE;
1✔
6635

6636
                /**
6637
                 * {@inheritDoc}
6638
                 */
6639
                public boolean isBound() {
6640
                    return false;
1✔
6641
                }
6642

6643
                /**
6644
                 * {@inheritDoc}
6645
                 */
6646
                public StackManipulation make(TypeList.Generic parameters, TypeDescription.Generic result, JavaConstant.MethodHandle methodHandle, StackManipulation stackManipulation, int freeOffset) {
UNCOV
6647
                    throw new IllegalStateException("Cannot resolve unresolved binding");
×
6648
                }
6649
            }
6650

6651
            /**
6652
             * A binding that was resolved for an actual substitution.
6653
             */
6654
            @HashCodeAndEqualsPlugin.Enhance
6655
            class Resolved implements Binding {
6656

6657
                /**
6658
                 * The type on which a field or method was accessed.
6659
                 */
6660
                private final TypeDescription receiver;
6661

6662
                /**
6663
                 * The field or method that was accessed.
6664
                 */
6665
                private final ByteCodeElement.Member original;
6666

6667
                /**
6668
                 * The substitution to apply.
6669
                 */
6670
                private final Substitution substitution;
6671

6672
                /**
6673
                 * Creates a new resolved binding.
6674
                 *
6675
                 * @param receiver     The type on which a field or method was accessed.
6676
                 * @param original     The field or method that was accessed.
6677
                 * @param substitution The substitution to apply.
6678
                 */
6679
                protected Resolved(TypeDescription receiver, ByteCodeElement.Member original, Substitution substitution) {
1✔
6680
                    this.receiver = receiver;
1✔
6681
                    this.original = original;
1✔
6682
                    this.substitution = substitution;
1✔
6683
                }
1✔
6684

6685
                /**
6686
                 * {@inheritDoc}
6687
                 */
6688
                public boolean isBound() {
6689
                    return true;
1✔
6690
                }
6691

6692
                /**
6693
                 * {@inheritDoc}
6694
                 */
6695
                public StackManipulation make(TypeList.Generic parameters, TypeDescription.Generic result, JavaConstant.MethodHandle methodHandle, StackManipulation stackManipulation, int freeOffset) {
6696
                    return substitution.resolve(receiver, original, parameters, result, methodHandle, stackManipulation, freeOffset);
1✔
6697
                }
6698
            }
6699
        }
6700

6701
        /**
6702
         * A factory for creating a replacement for an instrumented method.
6703
         */
6704
        interface Factory {
6705

6706
            /**
6707
             * Creates a replacement for an instrumented method.
6708
             *
6709
             * @param instrumentedType   The instrumented type.
6710
             * @param instrumentedMethod The instrumented method.
6711
             * @param typePool           The type pool being used within the member substitution being applied.
6712
             * @return A replacement to use within the supplied instrumented method.
6713
             */
6714
            Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool);
6715

6716
            /**
6717
             * A compound factory.
6718
             */
6719
            @HashCodeAndEqualsPlugin.Enhance
6720
            class Compound implements Factory {
6721

6722
                /**
6723
                 * A list of represented factories.
6724
                 */
6725
                private final List<Factory> factories;
6726

6727
                /**
6728
                 * Creates a new compound factory.
6729
                 *
6730
                 * @param factory A list of represented factories.
6731
                 */
6732
                protected Compound(Factory... factory) {
6733
                    this(Arrays.asList(factory));
1✔
6734
                }
1✔
6735

6736
                /**
6737
                 * Creates a new compound factory.
6738
                 *
6739
                 * @param factories A list of represented factories.
6740
                 */
6741
                protected Compound(List<? extends Factory> factories) {
1✔
6742
                    this.factories = new ArrayList<Factory>();
1✔
6743
                    for (Factory factory : factories) {
1✔
6744
                        if (factory instanceof Compound) {
1✔
UNCOV
6745
                            this.factories.addAll(((Compound) factory).factories);
×
6746
                        } else if (!(factory instanceof NoOp)) {
1✔
6747
                            this.factories.add(factory);
1✔
6748
                        }
6749
                    }
1✔
6750
                }
1✔
6751

6752
                /**
6753
                 * {@inheritDoc}
6754
                 */
6755
                public Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
6756
                    List<Replacement> replacements = new ArrayList<Replacement>();
1✔
6757
                    for (Factory factory : factories) {
1✔
6758
                        replacements.add(factory.make(instrumentedType, instrumentedMethod, typePool));
1✔
6759
                    }
1✔
6760
                    return new ForFirstBinding(replacements);
1✔
6761
                }
6762
            }
6763
        }
6764

6765
        /**
6766
         * Describes a method invocation type.
6767
         */
6768
        enum InvocationType {
1✔
6769

6770
            /**
6771
             * Describes a virtual method invocation.
6772
             */
6773
            VIRTUAL,
1✔
6774

6775
            /**
6776
             * Describes a super method invocation.
6777
             */
6778
            SUPER,
1✔
6779

6780
            /**
6781
             * Describes any method invocation that is not virtual or a super method invocation.
6782
             */
6783
            OTHER;
1✔
6784

6785
            /**
6786
             * Resolves an invocation type.
6787
             *
6788
             * @param opcode            The opcode that is used for invoking the method.
6789
             * @param methodDescription The method that is being invoked.
6790
             * @return The invocation type for the method given that opcode.
6791
             */
6792
            protected static InvocationType of(int opcode, MethodDescription methodDescription) {
6793
                switch (opcode) {
1✔
6794
                    case Opcodes.INVOKEVIRTUAL:
6795
                    case Opcodes.INVOKEINTERFACE:
6796
                        return InvocationType.VIRTUAL;
1✔
6797
                    case Opcodes.INVOKESPECIAL:
6798
                        return methodDescription.isVirtual() ? SUPER : OTHER;
1✔
6799
                    default:
6800
                        return OTHER;
1✔
6801
                }
6802
            }
6803

6804
            /**
6805
             * Checks if this invocation type matches the specified inputs.
6806
             *
6807
             * @param includeVirtualCalls {@code true} if a virtual method should be matched.
6808
             * @param includeSuperCalls   {@code true} if a super method call should be matched.
6809
             * @return {@code true} if this invocation type matches the specified parameters.
6810
             */
6811
            protected boolean matches(boolean includeVirtualCalls, boolean includeSuperCalls) {
6812
                switch (this) {
1✔
6813
                    case VIRTUAL:
6814
                        return includeVirtualCalls;
1✔
6815
                    case SUPER:
6816
                        return includeSuperCalls;
1✔
6817
                    default:
6818
                        return true;
1✔
6819
                }
6820
            }
6821
        }
6822

6823
        /**
6824
         * A non-operational replacement.
6825
         */
6826
        enum NoOp implements Replacement, Factory {
1✔
6827

6828
            /**
6829
             * The singleton instance.
6830
             */
6831
            INSTANCE;
1✔
6832

6833
            /**
6834
             * {@inheritDoc}
6835
             */
6836
            public Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
UNCOV
6837
                return this;
×
6838
            }
6839

6840
            /**
6841
             * {@inheritDoc}
6842
             */
6843
            public Binding bind(TypeDescription instrumentedType,
6844
                                MethodDescription instrumentedMethod,
6845
                                TypeDescription typeDescription,
6846
                                FieldDescription fieldDescription,
6847
                                boolean writeAccess) {
UNCOV
6848
                return Binding.Unresolved.INSTANCE;
×
6849
            }
6850

6851
            /**
6852
             * {@inheritDoc}
6853
             */
6854
            public Binding bind(TypeDescription instrumentedType,
6855
                                MethodDescription instrumentedMethod,
6856
                                TypeDescription typeDescription,
6857
                                MethodDescription methodDescription,
6858
                                InvocationType invocationType) {
UNCOV
6859
                return Binding.Unresolved.INSTANCE;
×
6860
            }
6861
        }
6862

6863
        /**
6864
         * A replacement that substitutes a member based on a row of element matchers.
6865
         */
6866
        @HashCodeAndEqualsPlugin.Enhance
6867
        class ForElementMatchers implements Replacement {
6868

6869
            /**
6870
             * The field matcher to consider when discovering fields.
6871
             */
6872
            private final ElementMatcher<? super FieldDescription> fieldMatcher;
6873

6874
            /**
6875
             * The method matcher to consider when discovering methods.
6876
             */
6877
            private final ElementMatcher<? super MethodDescription> methodMatcher;
6878

6879
            /**
6880
             * {@code true} if field reading access should be matched.
6881
             */
6882
            private final boolean matchFieldRead;
6883

6884
            /**
6885
             * {@code true} if field writing access should be matched.
6886
             */
6887
            private final boolean matchFieldWrite;
6888

6889
            /**
6890
             * {@code true} if virtual method calls should be matched.
6891
             */
6892
            private final boolean includeVirtualCalls;
6893

6894
            /**
6895
             * {@code true} if super method calls should be matched.
6896
             */
6897
            private final boolean includeSuperCalls;
6898

6899
            /**
6900
             * The substitution to trigger if a member is matched.
6901
             */
6902
            private final Substitution substitution;
6903

6904
            /**
6905
             * Creates a new replacement that triggers a substitution based on a row of matchers.
6906
             *
6907
             * @param fieldMatcher        The field matcher to consider when discovering fields.
6908
             * @param methodMatcher       The method matcher to consider when discovering methods.
6909
             * @param matchFieldRead      {@code true} if field reading access should be matched.
6910
             * @param matchFieldWrite     {@code true} if field writing access should be matched.
6911
             * @param includeVirtualCalls {@code true} if virtual method calls should be matched.
6912
             * @param includeSuperCalls   {@code true} if super method calls should be matched.
6913
             * @param substitution        The substitution to trigger if a member is matched.
6914
             */
6915
            protected ForElementMatchers(ElementMatcher<? super FieldDescription> fieldMatcher,
6916
                                         ElementMatcher<? super MethodDescription> methodMatcher,
6917
                                         boolean matchFieldRead,
6918
                                         boolean matchFieldWrite,
6919
                                         boolean includeVirtualCalls,
6920
                                         boolean includeSuperCalls,
6921
                                         Substitution substitution) {
1✔
6922
                this.fieldMatcher = fieldMatcher;
1✔
6923
                this.methodMatcher = methodMatcher;
1✔
6924
                this.matchFieldRead = matchFieldRead;
1✔
6925
                this.matchFieldWrite = matchFieldWrite;
1✔
6926
                this.includeVirtualCalls = includeVirtualCalls;
1✔
6927
                this.includeSuperCalls = includeSuperCalls;
1✔
6928
                this.substitution = substitution;
1✔
6929
            }
1✔
6930

6931
            /**
6932
             * {@inheritDoc}
6933
             */
6934
            public Binding bind(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypeDescription typeDescription, FieldDescription fieldDescription, boolean writeAccess) {
6935
                return (writeAccess ? matchFieldWrite : matchFieldRead) && fieldMatcher.matches(fieldDescription)
1✔
6936
                        ? new Binding.Resolved(typeDescription, fieldDescription, substitution)
6937
                        : Binding.Unresolved.INSTANCE;
6938
            }
6939

6940
            /**
6941
             * {@inheritDoc}
6942
             */
6943
            public Binding bind(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypeDescription typeDescription, MethodDescription methodDescription, InvocationType invocationType) {
6944
                return invocationType.matches(includeVirtualCalls, includeSuperCalls) && methodMatcher.matches(methodDescription)
1✔
6945
                        ? new Binding.Resolved(typeDescription, methodDescription, substitution)
6946
                        : Binding.Unresolved.INSTANCE;
6947
            }
6948

6949
            /**
6950
             * A factory for creating a replacement that chooses members based on a row of element matchers.
6951
             */
6952
            @HashCodeAndEqualsPlugin.Enhance
6953
            protected static class Factory implements Replacement.Factory {
6954

6955
                /**
6956
                 * The field matcher to consider when discovering fields.
6957
                 */
6958
                private final ElementMatcher<? super FieldDescription> fieldMatcher;
6959

6960
                /**
6961
                 * The method matcher to consider when discovering methods.
6962
                 */
6963
                private final ElementMatcher<? super MethodDescription> methodMatcher;
6964

6965
                /**
6966
                 * {@code true} if field reading access should be matched.
6967
                 */
6968
                private final boolean matchFieldRead;
6969

6970
                /**
6971
                 * {@code true} if field writing access should be matched.
6972
                 */
6973
                private final boolean matchFieldWrite;
6974

6975
                /**
6976
                 * {@code true} if virtual method calls should be matched.
6977
                 */
6978
                private final boolean includeVirtualCalls;
6979

6980
                /**
6981
                 * {@code true} if super method calls should be matched.
6982
                 */
6983
                private final boolean includeSuperCalls;
6984

6985
                /**
6986
                 * The substitution factory to create a substitution from.
6987
                 */
6988
                private final Substitution.Factory substitutionFactory;
6989

6990
                /**
6991
                 * Creates a new replacement that triggers a substitution based on a row of matchers.
6992
                 *
6993
                 * @param fieldMatcher        The field matcher to consider when discovering fields.
6994
                 * @param methodMatcher       The method matcher to consider when discovering methods.
6995
                 * @param matchFieldRead      {@code true} if field reading access should be matched.
6996
                 * @param matchFieldWrite     {@code true} if field writing access should be matched.
6997
                 * @param includeVirtualCalls {@code true} if virtual method calls should be matched.
6998
                 * @param includeSuperCalls   {@code true} if super method calls should be matched.
6999
                 * @param substitutionFactory The substitution factory to create a substitution from.
7000
                 */
7001
                protected Factory(ElementMatcher<? super FieldDescription> fieldMatcher,
7002
                                  ElementMatcher<? super MethodDescription> methodMatcher,
7003
                                  boolean matchFieldRead,
7004
                                  boolean matchFieldWrite,
7005
                                  boolean includeVirtualCalls,
7006
                                  boolean includeSuperCalls,
7007
                                  Substitution.Factory substitutionFactory) {
1✔
7008
                    this.fieldMatcher = fieldMatcher;
1✔
7009
                    this.methodMatcher = methodMatcher;
1✔
7010
                    this.matchFieldRead = matchFieldRead;
1✔
7011
                    this.matchFieldWrite = matchFieldWrite;
1✔
7012
                    this.includeVirtualCalls = includeVirtualCalls;
1✔
7013
                    this.includeSuperCalls = includeSuperCalls;
1✔
7014
                    this.substitutionFactory = substitutionFactory;
1✔
7015
                }
1✔
7016

7017
                /**
7018
                 * Creates a factory for applying a substitution on all matched byte code elements for all access types.
7019
                 *
7020
                 * @param matcher The matcher to apply.
7021
                 * @param factory The substitution factory to create a substitution from.
7022
                 * @return An appropriate replacement factory for the supplied matcher and substitution factory.
7023
                 */
7024
                protected static Replacement.Factory of(ElementMatcher<? super ByteCodeElement.Member> matcher, Substitution.Factory factory) {
UNCOV
7025
                    return new Factory(matcher, matcher, true, true, true, true, factory);
×
7026
                }
7027

7028
                /**
7029
                 * Creates a factory that only matches field access for given access types.
7030
                 *
7031
                 * @param matcher         The matcher to identify fields for substitution.
7032
                 * @param matchFieldRead  {@code true} if field read access should be matched.
7033
                 * @param matchFieldWrite {@code true} if field write access should be matched.
7034
                 * @param factory         The substitution factory to apply for fields that match the specified criteria.
7035
                 * @return An appropriate replacement factory.
7036
                 */
7037
                protected static Replacement.Factory ofField(ElementMatcher<? super FieldDescription> matcher, boolean matchFieldRead, boolean matchFieldWrite, Substitution.Factory factory) {
7038
                    return new Factory(matcher, none(), matchFieldRead, matchFieldWrite, false, false, factory);
1✔
7039
                }
7040

7041
                /**
7042
                 * Creates a factory that only matches method and constructor invocations for given invocation types.
7043
                 *
7044
                 * @param matcher             The matcher to identify methods and constructors for substitution.
7045
                 * @param includeVirtualCalls {@code true} if virtual method calls should be matched.
7046
                 * @param includeSuperCalls   {@code true} if super method calls should be matched.
7047
                 * @param factory             The substitution factory to apply for methods and constructors that match the specified criteria.
7048
                 * @return An appropriate replacement factory.
7049
                 */
7050
                protected static Replacement.Factory ofMethod(ElementMatcher<? super MethodDescription> matcher, boolean includeVirtualCalls, boolean includeSuperCalls, Substitution.Factory factory) {
7051
                    return new Factory(none(), matcher, false, false, includeVirtualCalls, includeSuperCalls, factory);
1✔
7052
                }
7053

7054
                /**
7055
                 * {@inheritDoc}
7056
                 */
7057
                public Replacement make(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypePool typePool) {
7058
                    return new ForElementMatchers(fieldMatcher,
1✔
7059
                            methodMatcher,
7060
                            matchFieldRead,
7061
                            matchFieldWrite,
7062
                            includeVirtualCalls,
7063
                            includeSuperCalls,
7064
                            substitutionFactory.make(instrumentedType, instrumentedMethod, typePool));
1✔
7065
                }
7066
            }
7067
        }
7068

7069
        /**
7070
         * A replacement that only resolves the first matching replacement of a list of replacements.
7071
         */
7072
        @HashCodeAndEqualsPlugin.Enhance
7073
        class ForFirstBinding implements Replacement {
7074

7075
            /**
7076
             * The list of replacements to consider.
7077
             */
7078
            private final List<? extends Replacement> replacements;
7079

7080
            /**
7081
             * Creates a new replacement that triggers the first matching replacement, if any.
7082
             *
7083
             * @param replacements The list of replacements to consider.
7084
             */
7085
            protected ForFirstBinding(List<? extends Replacement> replacements) {
1✔
7086
                this.replacements = replacements;
1✔
7087
            }
1✔
7088

7089
            /**
7090
             * {@inheritDoc}
7091
             */
7092
            public Binding bind(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypeDescription typeDescription, FieldDescription fieldDescription, boolean writeAccess) {
7093
                for (Replacement replacement : replacements) {
1✔
7094
                    Binding binding = replacement.bind(instrumentedType, instrumentedMethod, typeDescription, fieldDescription, writeAccess);
1✔
7095
                    if (binding.isBound()) {
1✔
7096
                        return binding;
1✔
7097
                    }
7098
                }
1✔
7099
                return Binding.Unresolved.INSTANCE;
1✔
7100
            }
7101

7102
            /**
7103
             * {@inheritDoc}
7104
             */
7105
            public Binding bind(TypeDescription instrumentedType, MethodDescription instrumentedMethod, TypeDescription typeDescription, MethodDescription methodDescription, InvocationType invocationType) {
7106
                for (Replacement replacement : replacements) {
1✔
7107
                    Binding binding = replacement.bind(instrumentedType, instrumentedMethod, typeDescription, methodDescription, invocationType);
1✔
7108
                    if (binding.isBound()) {
1✔
7109
                        return binding;
1✔
7110
                    }
7111
                }
1✔
7112
                return Binding.Unresolved.INSTANCE;
1✔
7113
            }
7114
        }
7115
    }
7116

7117
    /**
7118
     * A method visitor that applies a substitution for matched methods.
7119
     */
7120
    protected static class SubstitutingMethodVisitor extends LocalVariableAwareMethodVisitor {
7121

7122
        /**
7123
         * The instrumented type.
7124
         */
7125
        private final TypeDescription instrumentedType;
7126

7127
        /**
7128
         * The instrumented method.
7129
         */
7130
        private final MethodDescription instrumentedMethod;
7131

7132
        /**
7133
         * The method graph compiler to use.
7134
         */
7135
        private final MethodGraph.Compiler methodGraphCompiler;
7136

7137
        /**
7138
         * {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
7139
         */
7140
        private final boolean strict;
7141

7142
        /**
7143
         * {@code true} if the instrumentation should fail if applied to a method without match.
7144
         */
7145
        private final boolean failIfNoMatch;
7146

7147
        /**
7148
         * The replacement to use for creating substitutions.
7149
         */
7150
        private final Replacement replacement;
7151

7152
        /**
7153
         * The implementation context to use.
7154
         */
7155
        private final Implementation.Context implementationContext;
7156

7157
        /**
7158
         * The type pool to use.
7159
         */
7160
        private final TypePool typePool;
7161

7162
        /**
7163
         * If {@code true}, virtual method calls might target private methods in accordance to the nest mate specification.
7164
         */
7165
        private final boolean virtualPrivateCalls;
7166

7167
        /**
7168
         * An additional buffer for the operand stack that is required.
7169
         */
7170
        private int stackSizeBuffer;
7171

7172
        /**
7173
         * The minimum amount of local variable array slots that are required to apply substitutions.
7174
         */
7175
        private int localVariableExtension;
7176

7177
        /**
7178
         * {@code true} if at least one member was substituted during the application of this visitor.
7179
         */
7180
        private boolean matched;
7181

7182
        /**
7183
         * Creates a new substituting method visitor.
7184
         *
7185
         * @param methodVisitor         The method visitor to delegate to.
7186
         * @param instrumentedType      The instrumented type.
7187
         * @param instrumentedMethod    The instrumented method.
7188
         * @param methodGraphCompiler   The method graph compiler to use.
7189
         * @param strict                {@code true} if the method processing should be strict where an exception is raised if a member cannot be found.
7190
         * @param failIfNoMatch         {@code true} if the instrumentation should fail if applied to a method without match.
7191
         * @param replacement           The replacement to use for creating substitutions.
7192
         * @param implementationContext The implementation context to use.
7193
         * @param typePool              The type pool to use.
7194
         * @param virtualPrivateCalls   {@code true}, virtual method calls might target private methods in accordance to the nest mate specification.
7195
         */
7196
        protected SubstitutingMethodVisitor(MethodVisitor methodVisitor,
7197
                                            TypeDescription instrumentedType,
7198
                                            MethodDescription instrumentedMethod,
7199
                                            MethodGraph.Compiler methodGraphCompiler,
7200
                                            boolean strict,
7201
                                            boolean failIfNoMatch,
7202
                                            Replacement replacement,
7203
                                            Implementation.Context implementationContext,
7204
                                            TypePool typePool,
7205
                                            boolean virtualPrivateCalls) {
7206
            super(methodVisitor, instrumentedMethod);
1✔
7207
            this.instrumentedType = instrumentedType;
1✔
7208
            this.instrumentedMethod = instrumentedMethod;
1✔
7209
            this.methodGraphCompiler = methodGraphCompiler;
1✔
7210
            this.strict = strict;
1✔
7211
            this.failIfNoMatch = failIfNoMatch;
1✔
7212
            this.replacement = replacement;
1✔
7213
            this.implementationContext = implementationContext;
1✔
7214
            this.typePool = typePool;
1✔
7215
            this.virtualPrivateCalls = virtualPrivateCalls;
1✔
7216
            stackSizeBuffer = 0;
1✔
7217
            localVariableExtension = 0;
1✔
7218
        }
1✔
7219

7220
        @Override
7221
        public void visitFieldInsn(int opcode, String owner, String internalName, String descriptor) {
7222
            TypePool.Resolution resolution = typePool.describe(owner.replace('/', '.'));
1✔
7223
            if (resolution.isResolved()) {
1✔
7224
                FieldList<?> candidates;
7225
                Iterator<TypeDefinition> iterator = resolution.resolve().iterator();
1✔
7226
                do {
7227
                    candidates = iterator.next().getDeclaredFields().filter(strict
1✔
7228
                            ? ElementMatchers.<FieldDescription>named(internalName).and(hasDescriptor(descriptor))
1✔
UNCOV
7229
                            : ElementMatchers.<FieldDescription>failSafe(named(internalName).and(hasDescriptor(descriptor))));
×
7230
                } while (iterator.hasNext() && candidates.isEmpty());
1✔
7231
                if (!candidates.isEmpty()) {
1✔
7232
                    Replacement.Binding binding = replacement.bind(instrumentedType,
1✔
7233
                            instrumentedMethod,
7234
                            resolution.resolve(),
1✔
7235
                            candidates.getOnly(),
1✔
7236
                            opcode == Opcodes.PUTFIELD || opcode == Opcodes.PUTSTATIC);
7237
                    if (binding.isBound()) {
1✔
7238
                        TypeList.Generic parameters;
7239
                        TypeDescription.Generic result;
7240
                        boolean read;
7241
                        switch (opcode) {
1✔
7242
                            case Opcodes.PUTFIELD:
7243
                                parameters = new TypeList.Generic.Explicit(candidates.getOnly().getDeclaringType(), candidates.getOnly().getType());
1✔
7244
                                result = TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(void.class);
1✔
7245
                                read = false;
1✔
7246
                                break;
1✔
7247
                            case Opcodes.PUTSTATIC:
7248
                                parameters = new TypeList.Generic.Explicit(candidates.getOnly().getType());
1✔
7249
                                result = TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(void.class);
1✔
7250
                                read = false;
1✔
7251
                                break;
1✔
7252
                            case Opcodes.GETFIELD:
7253
                                parameters = new TypeList.Generic.Explicit(candidates.getOnly().getDeclaringType());
1✔
7254
                                result = candidates.getOnly().getType();
1✔
7255
                                read = true;
1✔
7256
                                break;
1✔
7257
                            case Opcodes.GETSTATIC:
7258
                                parameters = new TypeList.Generic.Empty();
1✔
7259
                                result = candidates.getOnly().getType();
1✔
7260
                                read = true;
1✔
7261
                                break;
1✔
7262
                            default:
UNCOV
7263
                                throw new IllegalStateException("Unexpected opcode: " + opcode);
×
7264
                        }
7265
                        stackSizeBuffer = Math.max(stackSizeBuffer, binding.make(parameters,
1✔
7266
                                result,
7267
                                read
7268
                                        ? JavaConstant.MethodHandle.ofGetter(candidates.getOnly().asDefined())
1✔
7269
                                        : JavaConstant.MethodHandle.ofSetter(candidates.getOnly().asDefined()),
1✔
7270
                                read
7271
                                        ? FieldAccess.forField(candidates.getOnly()).read()
1✔
7272
                                        : FieldAccess.forField(candidates.getOnly()).write(),
1✔
7273
                                getFreeOffset()).apply(new LocalVariableTracingMethodVisitor(mv), implementationContext).getMaximalSize());
1✔
7274
                        matched = true;
1✔
7275
                        return;
1✔
7276
                    }
7277
                } else if (strict) {
1✔
UNCOV
7278
                    throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + "." + internalName + descriptor + " using " + typePool);
×
7279
                }
7280
            } else if (strict) {
1✔
7281
                throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + " using " + typePool);
1✔
7282
            }
7283
            super.visitFieldInsn(opcode, owner, internalName, descriptor);
1✔
7284
        }
1✔
7285

7286
        @Override
7287
        public void visitMethodInsn(int opcode, String owner, String internalName, String descriptor, boolean isInterface) {
7288
            TypePool.Resolution resolution = typePool.describe(owner.replace('/', '.'));
1✔
7289
            if (resolution.isResolved()) {
1✔
7290
                MethodList<?> candidates;
7291
                if (opcode == Opcodes.INVOKESPECIAL && internalName.equals(MethodDescription.CONSTRUCTOR_INTERNAL_NAME)) {
1✔
7292
                    candidates = resolution.resolve().getDeclaredMethods().filter(strict
1✔
7293
                            ? ElementMatchers.<MethodDescription>isConstructor().and(hasDescriptor(descriptor))
1✔
UNCOV
7294
                            : ElementMatchers.<MethodDescription>failSafe(isConstructor().and(hasDescriptor(descriptor))));
×
7295
                } else if (opcode == Opcodes.INVOKESTATIC) {
1✔
7296
                    Iterator<TypeDefinition> iterator = resolution.resolve().iterator();
1✔
7297
                    do {
7298
                        candidates = iterator.next().getDeclaredMethods().filter(strict
1✔
7299
                                ? ElementMatchers.<MethodDescription>named(internalName).and(hasDescriptor(descriptor))
1✔
UNCOV
7300
                                : ElementMatchers.<MethodDescription>failSafe(named(internalName).and(hasDescriptor(descriptor))));
×
7301
                    } while (iterator.hasNext() && candidates.isEmpty());
1✔
7302
                } else if (opcode == Opcodes.INVOKESPECIAL) {
1✔
7303
                    candidates = resolution.resolve().getDeclaredMethods().filter(strict
1✔
7304
                            ? ElementMatchers.<MethodDescription>named(internalName).and(hasDescriptor(descriptor))
1✔
UNCOV
7305
                            : ElementMatchers.<MethodDescription>failSafe(named(internalName).and(hasDescriptor(descriptor))));
×
7306
                } else if (virtualPrivateCalls) {
1✔
UNCOV
7307
                    candidates = resolution.resolve().getDeclaredMethods().filter(strict
×
UNCOV
7308
                            ? ElementMatchers.<MethodDescription>isPrivate().and(not(isStatic())).and(named(internalName).and(hasDescriptor(descriptor)))
×
UNCOV
7309
                            : ElementMatchers.<MethodDescription>failSafe(isPrivate().<MethodDescription>and(not(isStatic())).and(named(internalName).and(hasDescriptor(descriptor)))));
×
UNCOV
7310
                    if (candidates.isEmpty()) {
×
UNCOV
7311
                        candidates = methodGraphCompiler.compile((TypeDefinition) resolution.resolve(), instrumentedType).listNodes().asMethodList().filter(strict
×
UNCOV
7312
                                ? ElementMatchers.<MethodDescription>named(internalName).and(hasDescriptor(descriptor))
×
UNCOV
7313
                                : ElementMatchers.<MethodDescription>failSafe(named(internalName).and(hasDescriptor(descriptor))));
×
7314
                    }
7315
                } else {
7316
                    candidates = methodGraphCompiler.compile((TypeDefinition) resolution.resolve(), instrumentedType).listNodes().asMethodList().filter(strict
1✔
7317
                            ? ElementMatchers.<MethodDescription>named(internalName).and(hasDescriptor(descriptor))
1✔
UNCOV
7318
                            : ElementMatchers.<MethodDescription>failSafe(named(internalName).and(hasDescriptor(descriptor))));
×
7319
                }
7320
                if (!candidates.isEmpty()) {
1✔
7321
                    Replacement.Binding binding = replacement.bind(instrumentedType,
1✔
7322
                            instrumentedMethod,
7323
                            resolution.resolve(),
1✔
7324
                            candidates.getOnly(),
1✔
7325
                            Replacement.InvocationType.of(opcode, candidates.getOnly()));
1✔
7326
                    if (binding.isBound()) {
1✔
7327
                        StackManipulation.Size size = binding.make(
1✔
7328
                                candidates.getOnly().isStatic() || candidates.getOnly().isConstructor()
1✔
7329
                                        ? candidates.getOnly().getParameters().asTypeList()
1✔
7330
                                        : new TypeList.Generic.Explicit(CompoundList.of(resolution.resolve(), candidates.getOnly().getParameters().asTypeList())),
1✔
7331
                                candidates.getOnly().isConstructor()
1✔
7332
                                        ? candidates.getOnly().getDeclaringType().asGenericType()
1✔
7333
                                        : candidates.getOnly().getReturnType(),
1✔
7334
                                opcode == Opcodes.INVOKESPECIAL && candidates.getOnly().isMethod() && !candidates.getOnly().isPrivate()
1✔
7335
                                        ? JavaConstant.MethodHandle.ofSpecial(candidates.getOnly().asDefined(), resolution.resolve())
1✔
7336
                                        : JavaConstant.MethodHandle.of(candidates.getOnly().asDefined()),
1✔
7337
                                opcode == Opcodes.INVOKESPECIAL && candidates.getOnly().isMethod() && !candidates.getOnly().isPrivate()
1✔
7338
                                        ? MethodInvocation.invoke(candidates.getOnly()).special(resolution.resolve())
1✔
7339
                                        : MethodInvocation.invoke(candidates.getOnly()), getFreeOffset()).apply(new LocalVariableTracingMethodVisitor(mv), implementationContext);
1✔
7340
                        if (candidates.getOnly().isConstructor()) {
1✔
7341
                            stackSizeBuffer = Math.max(stackSizeBuffer, size.getMaximalSize() + 2);
1✔
7342
                            stackSizeBuffer = Math.max(stackSizeBuffer, new StackManipulation.Compound(Duplication.SINGLE.flipOver(TypeDescription.ForLoadedType.of(Object.class)),
1✔
7343
                                    Removal.SINGLE,
7344
                                    Removal.SINGLE,
7345
                                    Duplication.SINGLE.flipOver(TypeDescription.ForLoadedType.of(Object.class)),
1✔
7346
                                    Removal.SINGLE,
7347
                                    Removal.SINGLE).apply(mv, implementationContext).getMaximalSize() + StackSize.SINGLE.getSize());
1✔
7348
                        } else {
7349
                            stackSizeBuffer = Math.max(stackSizeBuffer, size.getMaximalSize());
1✔
7350
                        }
7351
                        matched = true;
1✔
7352
                        return;
1✔
7353
                    }
7354
                } else if (strict) {
1✔
UNCOV
7355
                    throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + "." + internalName + descriptor + " using " + typePool);
×
7356
                }
7357
            } else if (strict) {
1✔
UNCOV
7358
                throw new IllegalStateException("Could not resolve " + owner.replace('/', '.') + " using " + typePool);
×
7359
            }
7360
            super.visitMethodInsn(opcode, owner, internalName, descriptor, isInterface);
1✔
7361
        }
1✔
7362

7363
        @Override
7364
        public void visitMaxs(int stackSize, int localVariableLength) {
7365
            if (failIfNoMatch && !matched) {
1✔
7366
                throw new IllegalStateException("No substitution found within " + instrumentedMethod + " of " + instrumentedType);
1✔
7367
            }
7368
            super.visitMaxs(stackSize + stackSizeBuffer, Math.max(localVariableExtension, localVariableLength));
1✔
7369
        }
1✔
7370

7371
        /**
7372
         * A method visitor that traces offsets of the local variable array being used.
7373
         */
7374
        private class LocalVariableTracingMethodVisitor extends MethodVisitor {
7375

7376
            /**
7377
             * Creates a new local variable tracing method visitor.
7378
             *
7379
             * @param methodVisitor The method visitor to delegate to.
7380
             */
7381
            private LocalVariableTracingMethodVisitor(MethodVisitor methodVisitor) {
1✔
7382
                super(OpenedClassReader.ASM_API, methodVisitor);
1✔
7383
            }
1✔
7384

7385
            @Override
7386
            @SuppressFBWarnings(value = "SF_SWITCH_NO_DEFAULT", justification = "No action required on default option.")
7387
            public void visitVarInsn(int opcode, int offset) {
7388
                switch (opcode) {
1✔
7389
                    case Opcodes.ISTORE:
7390
                    case Opcodes.FSTORE:
7391
                    case Opcodes.ASTORE:
7392
                        localVariableExtension = Math.max(localVariableExtension, offset + 1);
1✔
7393
                        break;
1✔
7394
                    case Opcodes.LSTORE:
7395
                    case Opcodes.DSTORE:
7396
                        localVariableExtension = Math.max(localVariableExtension, offset + 2);
1✔
7397
                        break;
7398
                }
7399
                super.visitVarInsn(opcode, offset);
1✔
7400
            }
1✔
7401
        }
7402
    }
7403

7404
    /**
7405
     * <p>
7406
     * Indicates that the annotated parameter should be mapped to the {@code this} reference of the substituted field,
7407
     * method, constructor or of the instrumented method.
7408
     * </p>
7409
     * <p>
7410
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.This} or
7411
     * {@link net.bytebuddy.asm.Advice.This}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7412
     * </p>
7413
     *
7414
     * @see Substitution.Chain.Step.ForDelegation
7415
     */
7416
    @Documented
7417
    @Retention(RetentionPolicy.RUNTIME)
7418
    @java.lang.annotation.Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.CONSTRUCTOR})
7419
    public @interface This {
7420

7421
        /**
7422
         * The typing that should be applied when assigning the {@code this} value.
7423
         *
7424
         * @return The typing to apply upon assignment.
7425
         */
7426
        Assigner.Typing typing() default Assigner.Typing.STATIC;
7427

7428
        /**
7429
         * Determines the source that is considered for this annotation which can be either the substituted method,
7430
         * constructor or field, or the instrumented method.
7431
         *
7432
         * @return The source that is considered for this annotation.
7433
         */
7434
        Source source() default Source.SUBSTITUTED_ELEMENT;
7435

7436
        /**
7437
         * Determines if the parameter should be assigned {@code null} if no {@code this} parameter is available.
7438
         *
7439
         * @return {@code true} if the value assignment is optional.
7440
         */
7441
        boolean optional() default false;
7442
    }
7443

7444
    /**
7445
     * <p>
7446
     * Indicates that the annotated parameter should be mapped to the parameter with index {@link Argument#value()}.
7447
     * </p>
7448
     * <p>
7449
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.Argument} or
7450
     * {@link net.bytebuddy.asm.Advice.Argument}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7451
     * </p>
7452
     *
7453
     * @see Substitution.Chain.Step.ForDelegation
7454
     */
7455
    @Documented
7456
    @Retention(RetentionPolicy.RUNTIME)
7457
    @java.lang.annotation.Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.CONSTRUCTOR})
7458
    public @interface Argument {
7459

7460
        /**
7461
         * Determines the index of the parameter that is being assigned.
7462
         *
7463
         * @return The index of the parameter that is being assigned.
7464
         */
7465
        int value();
7466

7467
        /**
7468
         * The typing that should be applied when assigning the argument.
7469
         *
7470
         * @return The typing to apply upon assignment.
7471
         */
7472
        Assigner.Typing typing() default Assigner.Typing.STATIC;
7473

7474
        /**
7475
         * Determines the source that is considered for this annotation which can be either the substituted method,
7476
         * constructor or field, or the instrumented method.
7477
         *
7478
         * @return The source that is considered for this annotation.
7479
         */
7480
        Source source() default Source.SUBSTITUTED_ELEMENT;
7481

7482
        /**
7483
         * Determines if the parameter should be assigned {@code null} if no argument with the specified index is available.
7484
         *
7485
         * @return {@code true} if the value assignment is optional.
7486
         */
7487
        boolean optional() default false;
7488
    }
7489

7490
    /**
7491
     * <p>
7492
     * Assigns an array containing all arguments of the targeted element to the annotated parameter. The annotated parameter must
7493
     * be an array type.
7494
     * </p>
7495
     * <p>
7496
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.AllArguments} or
7497
     * {@link net.bytebuddy.asm.Advice.AllArguments}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7498
     * </p>
7499
     *
7500
     * @see Substitution.Chain.Step.ForDelegation
7501
     */
7502
    @Documented
7503
    @Retention(RetentionPolicy.RUNTIME)
7504
    @java.lang.annotation.Target(ElementType.PARAMETER)
7505
    public @interface AllArguments {
7506

7507
        /**
7508
         * The typing that should be applied when assigning the arguments to an array element.
7509
         *
7510
         * @return The typing to apply upon assignment.
7511
         */
7512
        Assigner.Typing typing() default Assigner.Typing.STATIC;
7513

7514
        /**
7515
         * Determines the source that is considered for this annotation which can be either the substituted method,
7516
         * constructor or field, or the instrumented method.
7517
         *
7518
         * @return The source that is considered for this annotation.
7519
         */
7520
        Source source() default Source.SUBSTITUTED_ELEMENT;
7521

7522
        /**
7523
         * Determines if the produced array should include the instrumented method's target reference within the array, if
7524
         * the targeted element is non-static.
7525
         *
7526
         * @return {@code true} if a possible {@code this} reference should be included in the assigned array.
7527
         */
7528
        boolean includeSelf() default false;
7529

7530
        /**
7531
         * Determines if {@code null} should be assigned to the annotated parameter to the annotated parameter.
7532
         *
7533
         * @return {@code true} if {@code null} should be assigned to the annotated parameter to the annotated parameter.
7534
         */
7535
        boolean nullIfEmpty() default false;
7536
    }
7537

7538
    /**
7539
     * <p>
7540
     * Indicates that the annotated parameter should load a {@code java.lang.invoke.MethodHandle} that represents an invocation of
7541
     * the substituted expression or instrumented method. If the current method is virtual, it is bound to the current instance such
7542
     * that the virtual hierarchy is avoided. This annotation cannot be used to acquire a handle on enclosing constructors.
7543
     * </p>
7544
     * <p>
7545
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.asm.Advice.SelfCallHandle}. This annotation should
7546
     * be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7547
     * </p>
7548
     *
7549
     * @see Substitution.Chain.Step.ForDelegation
7550
     */
7551
    @Documented
7552
    @Retention(RetentionPolicy.RUNTIME)
7553
    @java.lang.annotation.Target(ElementType.PARAMETER)
7554
    public @interface SelfCallHandle {
7555

7556
        /**
7557
         * Determines the source that is considered for this annotation which can be either the substituted method,
7558
         * constructor or field, or the instrumented method.
7559
         *
7560
         * @return The source that is considered for this annotation.
7561
         */
7562
        Source source() default Source.SUBSTITUTED_ELEMENT;
7563

7564
        /**
7565
         * Determines if the method is bound to the arguments and instance of the represented invocation.
7566
         *
7567
         * @return {@code true} if the handle should be bound to the current arguments.
7568
         */
7569
        boolean bound() default true;
7570
    }
7571

7572
    /**
7573
     * <p>
7574
     * Indicates that the annotated parameter should be mapped to a field in the scope of the instrumented type.
7575
     * </p>
7576
     * <p>
7577
     * Setting {@link FieldValue#value()} is optional. If the value is not set, the field value attempts to bind a setter's
7578
     * or getter's field if the intercepted method is an accessor method. Otherwise, the binding renders the target method
7579
     * to be an illegal candidate for binding.
7580
     * </p>
7581
     * <p>
7582
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.FieldValue} or
7583
     * {@link net.bytebuddy.asm.Advice.FieldValue}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7584
     * </p>
7585
     *
7586
     * @see Substitution.Chain.Step.ForDelegation
7587
     */
7588
    @Documented
7589
    @Retention(RetentionPolicy.RUNTIME)
7590
    @java.lang.annotation.Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.CONSTRUCTOR})
7591
    public @interface FieldValue {
7592

7593
        /**
7594
         * Returns the name of the field.
7595
         *
7596
         * @return The name of the field.
7597
         */
7598
        String value() default Substitution.Chain.Step.ForDelegation.OffsetMapping.ForField.Unresolved.BEAN_PROPERTY;
7599

7600
        /**
7601
         * Returns the type that declares the field that should be mapped to the annotated parameter. If this property
7602
         * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy.
7603
         * The value can also be set to {@link TargetType} in order to look up the type on the instrumented type.
7604
         *
7605
         * @return The type that declares the field, {@code void} if this type should be determined implicitly or
7606
         * {@link TargetType} for the instrumented type.
7607
         */
7608
        Class<?> declaringType() default void.class;
7609

7610
        /**
7611
         * The typing that should be applied when assigning the field value.
7612
         *
7613
         * @return The typing to apply upon assignment.
7614
         */
7615
        Assigner.Typing typing() default Assigner.Typing.STATIC;
7616
    }
7617

7618
    /**
7619
     * <p>
7620
     * Indicates that the annotated parameter should be mapped to a {@code java.lang.invoke.MethodHandle} representing a field getter.
7621
     * </p>
7622
     * <p>
7623
     * Setting {@link FieldValue#value()} is optional. If the value is not set, the field value attempts to bind a setter's
7624
     * or getter's field if the intercepted method is an accessor method. Otherwise, the binding renders the target method
7625
     * to be an illegal candidate for binding.
7626
     * </p>
7627
     * <p>
7628
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.FieldGetterHandle} or
7629
     * {@link net.bytebuddy.asm.Advice.FieldGetterHandle}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7630
     * </p>
7631
     *
7632
     * @see Substitution.Chain.Step.ForDelegation
7633
     */
7634
    @Documented
7635
    @Retention(RetentionPolicy.RUNTIME)
7636
    @java.lang.annotation.Target(ElementType.PARAMETER)
7637
    public @interface FieldGetterHandle {
7638

7639
        /**
7640
         * Returns the name of the field.
7641
         *
7642
         * @return The name of the field.
7643
         */
7644
        String value() default Substitution.Chain.Step.ForDelegation.OffsetMapping.ForFieldHandle.Unresolved.BEAN_PROPERTY;
7645

7646
        /**
7647
         * Returns the type that declares the field that should be mapped to the annotated parameter. If this property
7648
         * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy.
7649
         * The value can also be set to {@link TargetType} in order to look up the type on the instrumented type.
7650
         *
7651
         * @return The type that declares the field, {@code void} if this type should be determined implicitly or
7652
         * {@link TargetType} for the instrumented type.
7653
         */
7654
        Class<?> declaringType() default void.class;
7655
    }
7656

7657
    /**
7658
     * <p>
7659
     * Indicates that the annotated parameter should be mapped to a {@code java.lang.invoke.MethodHandle} representing a field setter.
7660
     * </p>
7661
     * <p>
7662
     * Setting {@link FieldValue#value()} is optional. If the value is not set, the field value attempts to bind a setter's
7663
     * or getter's field if the intercepted method is an accessor method. Otherwise, the binding renders the target method
7664
     * to be an illegal candidate for binding.
7665
     * </p>
7666
     * <p>
7667
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.FieldSetterHandle} or
7668
     * {@link net.bytebuddy.asm.Advice.FieldSetterHandle}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7669
     * </p>
7670
     *
7671
     * @see Substitution.Chain.Step.ForDelegation
7672
     */
7673
    @Documented
7674
    @Retention(RetentionPolicy.RUNTIME)
7675
    @java.lang.annotation.Target(ElementType.PARAMETER)
7676
    public @interface FieldSetterHandle {
7677

7678
        /**
7679
         * Returns the name of the field.
7680
         *
7681
         * @return The name of the field.
7682
         */
7683
        String value() default Substitution.Chain.Step.ForDelegation.OffsetMapping.ForFieldHandle.Unresolved.BEAN_PROPERTY;
7684

7685
        /**
7686
         * Returns the type that declares the field that should be mapped to the annotated parameter. If this property
7687
         * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy.
7688
         * The value can also be set to {@link TargetType} in order to look up the type on the instrumented type.
7689
         *
7690
         * @return The type that declares the field, {@code void} if this type should be determined implicitly or
7691
         * {@link TargetType} for the instrumented type.
7692
         */
7693
        Class<?> declaringType() default void.class;
7694
    }
7695

7696
    /**
7697
     * <p>
7698
     * Indicates that the annotated parameter should be mapped to a representation of the substituted element or
7699
     * instrumented method. This representation can be a string representation, a constant representing
7700
     * the {@link Class}, a {@link Method}, {@link Constructor} or {@code java.lang.reflect.Executable}. It can also load
7701
     * a {@code java.lang.invoke.MethodType}, a {@code java.lang.invoke.MethodHandle} or a {@code java.lang.invoke.MethodHandles$Lookup}.
7702
     * </p>
7703
     * <p>
7704
     * <b>Note</b>: A constant representing a {@link Method} or {@link Constructor} is not cached but is recreated for
7705
     * every delegation.
7706
     * </p>
7707
     * <p>
7708
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.Origin} or
7709
     * {@link Advice.Origin}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7710
     * </p>
7711
     *
7712
     * @see Substitution.Chain.Step.ForDelegation
7713
     */
7714
    @Documented
7715
    @Retention(RetentionPolicy.RUNTIME)
7716
    @java.lang.annotation.Target(ElementType.PARAMETER)
7717
    public @interface Origin {
7718

7719
        /**
7720
         * Determines the source that is considered for this annotation which can be either the substituted method,
7721
         * constructor or field, or the instrumented method.
7722
         *
7723
         * @return The source that is considered for this annotation.
7724
         */
7725
        Source source() default Source.SUBSTITUTED_ELEMENT;
7726
    }
7727

7728
    /**
7729
     * <p>
7730
     * Indicates that the annotated parameter should always return a default value (i.e. {@code 0} for numeric values, {@code false}
7731
     * for {@code boolean} types and {@code null} for reference types).
7732
     * </p>
7733
     * <p>
7734
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.Empty} or
7735
     * {@link Advice.Unused}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7736
     * </p>
7737
     *
7738
     * @see Substitution.Chain.Step.ForDelegation
7739
     */
7740
    @Documented
7741
    @Retention(RetentionPolicy.RUNTIME)
7742
    @java.lang.annotation.Target(ElementType.PARAMETER)
7743
    public @interface Unused {
7744
        /* empty */
7745
    }
7746

7747
    /**
7748
     * <p>
7749
     * Indicates that the annotated parameter should always return a boxed version of the instrumented method's return value
7750
     * (i.e. {@code 0} for numeric values, {@code false} for {@code boolean} types and {@code null} for reference types). The annotated
7751
     * parameter must be of type {@link Object}.
7752
     * </p>
7753
     * <p>
7754
     * <b>Important</b>: Don't confuse this annotation with {@link net.bytebuddy.implementation.bind.annotation.StubValue} or
7755
     * {@link Advice.StubValue}. This annotation should be used only in combination with {@link Substitution.Chain.Step.ForDelegation}.
7756
     * </p>
7757
     *
7758
     * @see Substitution.Chain.Step.ForDelegation
7759
     */
7760
    @Documented
7761
    @Retention(RetentionPolicy.RUNTIME)
7762
    @java.lang.annotation.Target(ElementType.PARAMETER)
7763
    public @interface StubValue {
7764

7765
        /**
7766
         * Determines the source that is considered for this annotation which can be either the substituted method,
7767
         * constructor or field, or the instrumented method.
7768
         *
7769
         * @return The source that is considered for this annotation.
7770
         */
7771
        Source source() default Source.SUBSTITUTED_ELEMENT;
7772
    }
7773

7774
    /**
7775
     * Indicates that the annotated parameter should be assigned the value of the result that was
7776
     * yielded by the previous chain expression.
7777
     *
7778
     * @see Substitution.Chain.Step.ForDelegation
7779
     */
7780
    @Documented
7781
    @Retention(RetentionPolicy.RUNTIME)
7782
    @java.lang.annotation.Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.CONSTRUCTOR})
7783
    public @interface Current {
7784

7785
        /**
7786
         * The typing that should be applied when assigning the latest stack value.
7787
         *
7788
         * @return The typing to apply upon assignment.
7789
         */
7790
        Assigner.Typing typing() default Assigner.Typing.STATIC;
7791
    }
7792

7793
    /**
7794
     * Identifies the source of an instruction that might describe a value of the substituted element
7795
     * or the instrumented method.
7796
     */
7797
    public enum Source {
1✔
7798

7799
        /**
7800
         * Indicates that an element should be loaded in context of the substituted method, constructor or field.
7801
         */
7802
        SUBSTITUTED_ELEMENT {
1✔
7803
            @Override
7804
            protected ByteCodeElement.Member element(ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
UNCOV
7805
                return original;
×
7806
            }
7807

7808
            @Override
7809
            @MaybeNull
7810
            protected Source.Value self(TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
7811
                return original.isStatic()
1✔
7812
                        ? null
7813
                        : new Source.Value(parameters.get(THIS_REFERENCE), offsets.get(THIS_REFERENCE));
1✔
7814
            }
7815

7816
            @Override
7817
            @MaybeNull
7818
            protected Source.Value argument(int index, TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
7819
                return index < parameters.size() - (original.isStatic() ? 0 : 1)
1✔
7820
                        ? new Source.Value(parameters.get(index + (original.isStatic() ? 0 : 1)), offsets.get(index + (original.isStatic() ? 0 : 1)))
1✔
7821
                        : null;
7822
            }
7823

7824
            @Override
7825
            protected List<Source.Value> arguments(boolean includesSelf,
7826
                                                   TypeList.Generic parameters,
7827
                                                   Map<Integer, Integer> offsets,
7828
                                                   ByteCodeElement.Member original,
7829
                                                   MethodDescription instrumentedMethod) {
7830
                List<Source.Value> values = new ArrayList<Source.Value>(parameters.size() - (!includesSelf && !original.isStatic() ? 1 : 0));
1✔
7831
                for (int index = original.isStatic() || includesSelf ? 0 : 1; index < parameters.size(); index++) {
1✔
7832
                    values.add(new Source.Value(parameters.get(index), offsets.get(index)));
1✔
7833
                }
7834
                return values;
1✔
7835
            }
7836

7837
            @Override
7838
            protected JavaConstant.MethodHandle handle(JavaConstant.MethodHandle methodHandle, MethodDescription instrumentedMethod) {
7839
                return methodHandle;
1✔
7840
            }
7841

7842
            @Override
7843
            protected boolean isRepresentable(Substitution.Chain.Step.ForDelegation.OffsetMapping.ForOrigin.Sort sort, ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
7844
                return sort.isRepresentable(original);
1✔
7845
            }
7846

7847
            @Override
7848
            protected StackManipulation resolve(Substitution.Chain.Step.ForDelegation.OffsetMapping.ForOrigin.Sort sort,
7849
                                                ByteCodeElement.Member original,
7850
                                                TypeList.Generic parameters,
7851
                                                TypeDescription.Generic result,
7852
                                                MethodDescription instrumentedMethod) {
7853
                return sort.resolve(original, parameters.asErasures(), result.asErasure());
1✔
7854
            }
7855
        },
7856

7857
        /**
7858
         * Indicates that an element should be loaded in context of the instrumented method.
7859
         */
7860
        ENCLOSING_METHOD {
1✔
7861
            @Override
7862
            protected ByteCodeElement.Member element(ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
UNCOV
7863
                return instrumentedMethod;
×
7864
            }
7865

7866
            @Override
7867
            @MaybeNull
7868
            protected Source.Value self(TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
7869
                return instrumentedMethod.isStatic()
1✔
7870
                        ? null
7871
                        : new Source.Value(instrumentedMethod.getDeclaringType().asGenericType(), THIS_REFERENCE);
1✔
7872
            }
7873

7874
            @Override
7875
            @MaybeNull
7876
            protected Source.Value argument(int index, TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
7877
                if (index < instrumentedMethod.getParameters().size()) {
1✔
7878
                    ParameterDescription parameterDescription = instrumentedMethod.getParameters().get(index);
1✔
7879
                    return new Source.Value(parameterDescription.getType(), parameterDescription.getOffset());
1✔
7880
                } else {
UNCOV
7881
                    return null;
×
7882
                }
7883
            }
7884

7885
            @Override
7886
            protected List<Source.Value> arguments(boolean includesSelf, TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
7887
                List<Source.Value> values;
7888
                if (includesSelf && !instrumentedMethod.isStatic()) {
1✔
UNCOV
7889
                    values = new ArrayList<Source.Value>(instrumentedMethod.getParameters().size() + 1);
×
UNCOV
7890
                    values.add(new Source.Value(instrumentedMethod.getDeclaringType().asGenericType(), THIS_REFERENCE));
×
7891
                } else {
7892
                    values = new ArrayList<Source.Value>(instrumentedMethod.getParameters().size());
1✔
7893
                }
7894
                for (ParameterDescription parameterDescription : instrumentedMethod.getParameters()) {
1✔
7895
                    values.add(new Source.Value(parameterDescription.getType(), parameterDescription.getOffset()));
1✔
7896
                }
1✔
7897
                return values;
1✔
7898
            }
7899

7900
            @Override
7901
            protected JavaConstant.MethodHandle handle(JavaConstant.MethodHandle methodHandle, MethodDescription instrumentedMethod) {
UNCOV
7902
                return JavaConstant.MethodHandle.of(instrumentedMethod.asDefined());
×
7903
            }
7904

7905
            @Override
7906
            protected boolean isRepresentable(Substitution.Chain.Step.ForDelegation.OffsetMapping.ForOrigin.Sort sort, ByteCodeElement.Member original, MethodDescription instrumentedMethod) {
7907
                return sort.isRepresentable(instrumentedMethod);
1✔
7908
            }
7909

7910
            @Override
7911
            protected StackManipulation resolve(Substitution.Chain.Step.ForDelegation.OffsetMapping.ForOrigin.Sort sort,
7912
                                                ByteCodeElement.Member original,
7913
                                                TypeList.Generic parameters,
7914
                                                TypeDescription.Generic result,
7915
                                                MethodDescription instrumentedMethod) {
7916
                return sort.resolve(instrumentedMethod,
1✔
7917
                        instrumentedMethod.isStatic() || instrumentedMethod.isConstructor()
1✔
7918
                                ? instrumentedMethod.getParameters().asTypeList().asErasures()
1✔
7919
                                : CompoundList.of(instrumentedMethod.getDeclaringType().asErasure(), instrumentedMethod.getParameters().asTypeList().asErasures()),
1✔
7920
                        instrumentedMethod.isConstructor()
1✔
7921
                                ? instrumentedMethod.getDeclaringType().asErasure()
1✔
7922
                                : instrumentedMethod.getReturnType().asErasure());
1✔
7923
            }
7924
        };
7925

7926
        /**
7927
         * Resolves the targeted byte code element.
7928
         *
7929
         * @param original           The substituted element.
7930
         * @param instrumentedMethod The instrumented element.
7931
         * @return The byte code element that is represented by this source.
7932
         */
7933
        protected abstract ByteCodeElement.Member element(ByteCodeElement.Member original, MethodDescription instrumentedMethod);
7934

7935
        /**
7936
         * Resolves a value representation of the {@code this} reference or {@code null} if no such reference is available.
7937
         *
7938
         * @param parameters         The list of parameters of the substituted element.
7939
         * @param offsets            A mapping of offsets of parameter indices to offsets.
7940
         * @param original           The substituted element.
7941
         * @param instrumentedMethod The instrumented method.
7942
         * @return A representation of the {@code this} reference or {@code null} if no such reference is available.
7943
         */
7944
        @MaybeNull
7945
        protected abstract Source.Value self(TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod);
7946

7947
        /**
7948
         * Resolves a value representation of the parameter of the specified index or {@code null} if no such parameter is available.
7949
         *
7950
         * @param index              The index of the targeted parameter.
7951
         * @param parameters         The list of parameters of the substituted element.
7952
         * @param offsets            A mapping of offsets of parameter indices to offsets.
7953
         * @param original           The substituted element.
7954
         * @param instrumentedMethod The instrumented method.
7955
         * @return A representation of the parameter of the specified index or {@code null} if no such parameter is available.
7956
         */
7957
        @MaybeNull
7958
        protected abstract Source.Value argument(int index, TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod);
7959

7960
        /**
7961
         * Resolves a list of value representation of all parameters.
7962
         *
7963
         * @param includesSelf       {@code true} if the {@code this} reference should be included if available.
7964
         * @param parameters         The list of parameters of the substituted element.
7965
         * @param offsets            A mapping of offsets of parameter indices to offsets.
7966
         * @param original           The substituted element.
7967
         * @param instrumentedMethod The instrumented method.
7968
         * @return A list of representation of all values of all parameters.
7969
         */
7970
        protected abstract List<Source.Value> arguments(boolean includesSelf, TypeList.Generic parameters, Map<Integer, Integer> offsets, ByteCodeElement.Member original, MethodDescription instrumentedMethod);
7971

7972
        /**
7973
         * Resolves a method handle.
7974
         *
7975
         * @param methodHandle       A method handle of the substituted element.
7976
         * @param instrumentedMethod The instrumented method.
7977
         * @return An appropriate method handle.
7978
         */
7979
        protected abstract JavaConstant.MethodHandle handle(JavaConstant.MethodHandle methodHandle, MethodDescription instrumentedMethod);
7980

7981
        /**
7982
         * Validates if the supplied origin sort is representable.
7983
         *
7984
         * @param sort               The sort of origin.
7985
         * @param original           The substituted element.
7986
         * @param instrumentedMethod The instrumented method.
7987
         * @return {@code true} if the supplied sort of origin is representable.
7988
         */
7989
        protected abstract boolean isRepresentable(Substitution.Chain.Step.ForDelegation.OffsetMapping.ForOrigin.Sort sort, ByteCodeElement.Member original, MethodDescription instrumentedMethod);
7990

7991
        /**
7992
         * Resolves a stack manipulation that loads the supplied sort of origin onto the operand stack.
7993
         *
7994
         * @param sort               The sort of origin.
7995
         * @param original           The substituted element.
7996
         * @param parameters         The parameters to the substituted element.
7997
         * @param result             The type upon which the substituted element is invoked.
7998
         * @param instrumentedMethod The instrumented method.
7999
         * @return A stack manipulation loading the supplied sort of origin onto the operand stack.
8000
         */
8001
        protected abstract StackManipulation resolve(Substitution.Chain.Step.ForDelegation.OffsetMapping.ForOrigin.Sort sort,
8002
                                                     ByteCodeElement.Member original,
8003
                                                     TypeList.Generic parameters,
8004
                                                     TypeDescription.Generic result,
8005
                                                     MethodDescription instrumentedMethod);
8006

8007
        /**
8008
         * Represents a value that can be loaded from a given offset.
8009
         */
8010
        @HashCodeAndEqualsPlugin.Enhance
8011
        protected static class Value {
8012

8013
            /**
8014
             * The type of the loaded value.
8015
             */
8016
            private final TypeDescription.Generic typeDescription;
8017

8018
            /**
8019
             * The offset of the loaded value.
8020
             */
8021
            private final int offset;
8022

8023
            /**
8024
             * Creates a value representation.
8025
             *
8026
             * @param typeDescription The type of the loaded value.
8027
             * @param offset          The offset of the loaded value.
8028
             */
8029
            protected Value(TypeDescription.Generic typeDescription, int offset) {
1✔
8030
                this.typeDescription = typeDescription;
1✔
8031
                this.offset = offset;
1✔
8032
            }
1✔
8033

8034
            /**
8035
             * Returns the type of the loaded value.
8036
             *
8037
             * @return The type of the loaded value.
8038
             */
8039
            protected TypeDescription.Generic getTypeDescription() {
8040
                return typeDescription;
1✔
8041
            }
8042

8043
            /**
8044
             * Returns the offset of the loaded value.
8045
             *
8046
             * @return The offset of the loaded value.
8047
             */
8048
            protected int getOffset() {
8049
                return offset;
1✔
8050
            }
8051
        }
8052
    }
8053
}
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