1 /*
2  * Copyright 2014 - 2020 Rafael Winterhalter
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package net.bytebuddy.implementation;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.field.FieldDescription;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.dynamic.scaffold.FieldLocator;
23 import net.bytebuddy.dynamic.scaffold.InstrumentedType;
24 import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
25 import net.bytebuddy.implementation.bytecode.StackManipulation;
26 import net.bytebuddy.implementation.bytecode.assign.Assigner;
27 import net.bytebuddy.implementation.bytecode.constant.*;
28 import net.bytebuddy.implementation.bytecode.member.FieldAccess;
29 import net.bytebuddy.implementation.bytecode.member.MethodReturn;
30 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
31 import net.bytebuddy.utility.JavaConstant;
32 import net.bytebuddy.utility.JavaType;
33 import net.bytebuddy.utility.RandomString;
34 import net.bytebuddy.jar.asm.MethodVisitor;
35 import net.bytebuddy.jar.asm.Opcodes;
36
37 import java.lang.reflect.Field;
38 import java.lang.reflect.Type;
39
40 import static net.bytebuddy.matcher.ElementMatchers.named;
41
42 /**
43  * <p>
44  * Defines a method to access a given field by following the Java bean conventions for getters and setters:
45  * </p>
46  * <ul>
47  * <li>Getter: A method named {@code getFoo()} will be instrumented to read and return the value of a field {@code foo}
48  * or another field if one was specified explicitly. If a property is of type {@link java.lang.Boolean} or
49  * {@code boolean}, the name {@code isFoo()} is also permitted.</li>
50  * <li>Setter: A method named {@code setFoo(value)} will be instrumented to write the given argument {@code value}
51  * to a field {@code foo} or to another field if one was specified explicitly.</li>
52  * </ul>
53  * <p>
54  * Field accessors always implement a getter if a non-{@code void} value is returned from a method and attempt to define a setter
55  * otherwise. If a field accessor is not explicitly defined as a setter via {@link PropertyConfigurable}, an instrumented
56  * method must define exactly one parameter. Using the latter API, an explicit parameter index can be defined and a return
57  * value can be specified explicitly when {@code void} is not returned.
58  * </p>
59  */

60 @HashCodeAndEqualsPlugin.Enhance
61 public abstract class FieldAccessor implements Implementation {
62
63     /**
64      * The field's location.
65      */

66     protected final FieldLocation fieldLocation;
67
68     /**
69      * The assigner to use.
70      */

71     protected final Assigner assigner;
72
73     /**
74      * Indicates if dynamic type castings should be attempted for incompatible assignments.
75      */

76     protected final Assigner.Typing typing;
77
78     /**
79      * Creates a new field accessor.
80      *
81      * @param fieldLocation The field's location.
82      * @param assigner      The assigner to use.
83      * @param typing        Indicates if dynamic type castings should be attempted for incompatible assignments.
84      */

85     protected FieldAccessor(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing) {
86         this.fieldLocation = fieldLocation;
87         this.assigner = assigner;
88         this.typing = typing;
89     }
90
91     /**
92      * Defines a field accessor where any access is targeted to a field named {@code name}.
93      *
94      * @param name The name of the field to be accessed.
95      * @return A field accessor for a field of a given name.
96      */

97     public static OwnerTypeLocatable ofField(String name) {
98         return of(new FieldNameExtractor.ForFixedValue(name));
99     }
100
101     /**
102      * Defines a field accessor where any access is targeted to a field that matches the methods
103      * name with the Java specification for bean properties, i.e. a method {@code getFoo} or {@code setFoo(value)}
104      * will either read or write a field named {@code foo}.
105      *
106      * @return A field accessor that follows the Java naming conventions for bean properties.
107      */

108     public static OwnerTypeLocatable ofBeanProperty() {
109         return of(FieldNameExtractor.ForBeanProperty.INSTANCE);
110     }
111
112     /**
113      * Defines a custom strategy for determining the field that is accessed by this field accessor.
114      *
115      * @param fieldNameExtractor The field name extractor to use.
116      * @return A field accessor using the given field name extractor.
117      */

118     public static OwnerTypeLocatable of(FieldNameExtractor fieldNameExtractor) {
119         return new ForImplicitProperty(new FieldLocation.Relative(fieldNameExtractor));
120     }
121
122     /**
123      * Defines a field accessor where the specified field is accessed. The field must be within the hierarchy of the instrumented type.
124      *
125      * @param field The field being accessed.
126      * @return A field accessor for the given field.
127      */

128     public static AssignerConfigurable of(Field field) {
129         return of(new FieldDescription.ForLoadedField(field));
130     }
131
132     /**
133      * Defines a field accessor where the specified field is accessed. The field must be within the hierarchy of the instrumented type.
134      *
135      * @param fieldDescription The field being accessed.
136      * @return A field accessor for the given field.
137      */

138     public static AssignerConfigurable of(FieldDescription fieldDescription) {
139         return new ForImplicitProperty(new FieldLocation.Absolute(fieldDescription));
140     }
141
142     /**
143      * A field location represents an identified field description which depends on the instrumented type and method.
144      */

145     protected interface FieldLocation {
146
147         /**
148          * Specifies a field locator factory to use.
149          *
150          * @param fieldLocatorFactory The field locator factory to use.
151          * @return An appropriate field location.
152          */

153         FieldLocation with(FieldLocator.Factory fieldLocatorFactory);
154
155         /**
156          * A prepared field location.
157          *
158          * @param instrumentedType The instrumented type.
159          * @return A prepared field location.
160          */

161         Prepared prepare(TypeDescription instrumentedType);
162
163         /**
164          * A prepared field location.
165          */

166         interface Prepared {
167
168             /**
169              * Resolves the field description to use.
170              *
171              * @param instrumentedMethod The instrumented method.
172              * @return The resolved field description.
173              */

174             FieldDescription resolve(MethodDescription instrumentedMethod);
175         }
176
177         /**
178          * An absolute field description representing a previously resolved field.
179          */

180         @HashCodeAndEqualsPlugin.Enhance
181         class Absolute implements FieldLocation, Prepared {
182
183             /**
184              * The field description.
185              */

186             private final FieldDescription fieldDescription;
187
188             /**
189              * Creates an absolute field location.
190              *
191              * @param fieldDescription The field description.
192              */

193             protected Absolute(FieldDescription fieldDescription) {
194                 this.fieldDescription = fieldDescription;
195             }
196
197             /**
198              * {@inheritDoc}
199              */

200             public FieldLocation with(FieldLocator.Factory fieldLocatorFactory) {
201                 throw new IllegalStateException("Cannot specify a field locator factory for an absolute field location");
202             }
203
204             /**
205              * {@inheritDoc}
206              */

207             public Prepared prepare(TypeDescription instrumentedType) {
208                 if (!fieldDescription.isStatic() && !instrumentedType.isAssignableTo(fieldDescription.getDeclaringType().asErasure())) {
209                     throw new IllegalStateException(fieldDescription + " is not declared by " + instrumentedType);
210                 } else if (!fieldDescription.isAccessibleTo(instrumentedType)) {
211                     throw new IllegalStateException("Cannot access " + fieldDescription + " from " + instrumentedType);
212                 }
213                 return this;
214             }
215
216             /**
217              * {@inheritDoc}
218              */

219             public FieldDescription resolve(MethodDescription instrumentedMethod) {
220                 return fieldDescription;
221             }
222         }
223
224         /**
225          * A relative field location where a field is located dynamically.
226          */

227         @HashCodeAndEqualsPlugin.Enhance
228         class Relative implements FieldLocation {
229
230             /**
231              * The field name extractor to use.
232              */

233             private final FieldNameExtractor fieldNameExtractor;
234
235             /**
236              * The field locator factory to use.
237              */

238             private final FieldLocator.Factory fieldLocatorFactory;
239
240             /**
241              * Creates a new relative field location.
242              *
243              * @param fieldNameExtractor The field name extractor to use.
244              */

245             protected Relative(FieldNameExtractor fieldNameExtractor) {
246                 this(fieldNameExtractor, FieldLocator.ForClassHierarchy.Factory.INSTANCE);
247             }
248
249             /**
250              * Creates a new relative field location.
251              *
252              * @param fieldNameExtractor  The field name extractor to use.
253              * @param fieldLocatorFactory The field locator factory to use.
254              */

255             private Relative(FieldNameExtractor fieldNameExtractor, FieldLocator.Factory fieldLocatorFactory) {
256                 this.fieldNameExtractor = fieldNameExtractor;
257                 this.fieldLocatorFactory = fieldLocatorFactory;
258             }
259
260             /**
261              * {@inheritDoc}
262              */

263             public FieldLocation with(FieldLocator.Factory fieldLocatorFactory) {
264                 return new Relative(fieldNameExtractor, fieldLocatorFactory);
265             }
266
267             /**
268              * {@inheritDoc}
269              */

270             public FieldLocation.Prepared prepare(TypeDescription instrumentedType) {
271                 return new Prepared(fieldNameExtractor, fieldLocatorFactory.make(instrumentedType));
272             }
273
274             /**
275              * A prepared version of a field location.
276              */

277             @HashCodeAndEqualsPlugin.Enhance
278             protected static class Prepared implements FieldLocation.Prepared {
279
280                 /**
281                  * The field name extractor to use.
282                  */

283                 private final FieldNameExtractor fieldNameExtractor;
284
285                 /**
286                  * The field locator factory to use.
287                  */

288                 private final FieldLocator fieldLocator;
289
290                 /**
291                  * Creates a new relative field location.
292                  *
293                  * @param fieldNameExtractor The field name extractor to use.
294                  * @param fieldLocator       The field locator to use.
295                  */

296                 protected Prepared(FieldNameExtractor fieldNameExtractor, FieldLocator fieldLocator) {
297                     this.fieldNameExtractor = fieldNameExtractor;
298                     this.fieldLocator = fieldLocator;
299                 }
300
301                 /**
302                  * {@inheritDoc}
303                  */

304                 public FieldDescription resolve(MethodDescription instrumentedMethod) {
305                     FieldLocator.Resolution resolution = fieldLocator.locate(fieldNameExtractor.resolve(instrumentedMethod));
306                     if (!resolution.isResolved()) {
307                         throw new IllegalStateException("Cannot resolve field for " + instrumentedMethod + " using " + fieldLocator);
308                     }
309                     return resolution.getField();
310                 }
311             }
312         }
313     }
314
315     /**
316      * A field name extractor is responsible for determining a field name to a method that is implemented
317      * to access this method.
318      */

319     public interface FieldNameExtractor {
320
321         /**
322          * Extracts a field name to be accessed by a getter or setter method.
323          *
324          * @param methodDescription The method for which a field name is to be determined.
325          * @return The name of the field to be accessed by this method.
326          */

327         String resolve(MethodDescription methodDescription);
328
329         /**
330          * A {@link net.bytebuddy.implementation.FieldAccessor.FieldNameExtractor} that determines a field name
331          * according to the rules of Java bean naming conventions.
332          */

333         enum ForBeanProperty implements FieldNameExtractor {
334
335             /**
336              * The singleton instance.
337              */

338             INSTANCE;
339
340             /**
341              * {@inheritDoc}
342              */

343             public String resolve(MethodDescription methodDescription) {
344                 String name = methodDescription.getInternalName();
345                 int crop;
346                 if (name.startsWith("get") || name.startsWith("set")) {
347                     crop = 3;
348                 } else if (name.startsWith("is")) {
349                     crop = 2;
350                 } else {
351                     throw new IllegalArgumentException(methodDescription + " does not follow Java bean naming conventions");
352                 }
353                 name = name.substring(crop);
354                 if (name.length() == 0) {
355                     throw new IllegalArgumentException(methodDescription + " does not specify a bean name");
356                 }
357                 return Character.toLowerCase(name.charAt(0)) + name.substring(1);
358             }
359         }
360
361         /**
362          * A field name extractor that returns a fixed value.
363          */

364         @HashCodeAndEqualsPlugin.Enhance
365         class ForFixedValue implements FieldNameExtractor {
366
367             /**
368              * The name to return.
369              */

370             private final String name;
371
372             /**
373              * Creates a new field name extractor for a fixed value.
374              *
375              * @param name The name to return.
376              */

377             protected ForFixedValue(String name) {
378                 this.name = name;
379             }
380
381             /**
382              * {@inheritDoc}
383              */

384             public String resolve(MethodDescription methodDescription) {
385                 return name;
386             }
387         }
388     }
389
390     /**
391      * A field accessor that allows to define the access to be a field write of a given argument.
392      */

393     public interface PropertyConfigurable extends Implementation {
394
395         /**
396          * <p>
397          * Defines a setter of the specified parameter for the field being described.
398          * </p>
399          * <p>
400          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
401          * </p>
402          *
403          * @param index The index of the parameter for which to set the field's value.
404          * @return An instrumentation that sets the parameter's value to the described field.
405          */

406         Composable setsArgumentAt(int index);
407
408         /**
409          * <p>
410          * Defines a setter of the described field's default value, i.e. {@code null} or a primitive type's
411          * representation of {@code 0}.
412          * </p>
413          * <p>
414          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
415          * </p>
416          *
417          * @return An instrumentation that sets the field's default value.
418          */

419         Composable setsDefaultValue();
420
421         /**
422          * <p>
423          * Defines a setter of a given value for the described field. If the value is a constant value, it will be
424          * defined as a constant assignment, otherwise it is defined as a reference value that is stored in a static
425          * field of the instrumented type.
426          * </p>
427          * <p>
428          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
429          * </p>
430          *
431          * @param value The value to set.
432          * @return An instrumentation that sets the field's value as specified.
433          */

434         Composable setsValue(Object value);
435
436         /**
437          * <p>
438          * Defines a setter of a given class constant value for the described field.
439          * </p>
440          * <p>
441          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
442          * </p>
443          *
444          * @param typeDescription The type to set to the described field.
445          * @return An instrumentation that sets the field's value to the given class constant.
446          */

447         Composable setsValue(TypeDescription typeDescription);
448
449         /**
450          * <p>
451          * Defines a setter of a given constant value for the described field.
452          * </p>
453          * <p>
454          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
455          * </p>
456          *
457          * @param constant The constant to set as a value.
458          * @return An instrumentation that sets the field's value to the given constant.
459          */

460         Composable setsValue(JavaConstant constant);
461
462         /**
463          * <p>
464          * Defines a setter of a value that is represented by a stack manipulation.
465          * </p>
466          * <p>
467          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
468          * </p>
469          *
470          * @param stackManipulation A stack manipulation to load the field's value.
471          * @param type              The field value's type.
472          * @return An instrumentation that sets the field's value to the given value.
473          */

474         Composable setsValue(StackManipulation stackManipulation, Type type);
475
476         /**
477          * <p>
478          * Defines a setter of a value that is represented by a stack manipulation.
479          * </p>
480          * <p>
481          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
482          * </p>
483          *
484          * @param stackManipulation A stack manipulation to load the field's value.
485          * @param typeDescription   The field value's type.
486          * @return An instrumentation that sets the field's value to the given value.
487          */

488         Composable setsValue(StackManipulation stackManipulation, TypeDescription.Generic typeDescription);
489
490         /**
491          * <p>
492          * Defines a setter of a given value for the described field. The value is kept as a referenced that is stored
493          * in a static field of the instrumented type. The field name is chosen based on the value's hash code.
494          * </p>
495          * <p>
496          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
497          * </p>
498          *
499          * @param value The value to set.
500          * @return An instrumentation that sets the field's value as specified.
501          */

502         Composable setsReference(Object value);
503
504         /**
505          * <p>
506          * Defines a setter of a given value for the described field. The value is kept as a referenced that is stored
507          * in a static field of the instrumented type.
508          * </p>
509          * <p>
510          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
511          * </p>
512          *
513          * @param value The value to set.
514          * @param name  The name of the field.
515          * @return An instrumentation that sets the field's value as specified.
516          */

517         Composable setsReference(Object value, String name);
518
519         /**
520          * <p>
521          * Defines a setter of a value that sets another field's value.
522          * </p>
523          * <p>
524          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
525          * </p>
526          *
527          * @param field The field that holds the value to be set.
528          * @return An instrumentation that sets the field's value to the specified field's value.
529          */

530         Composable setsFieldValueOf(Field field);
531
532         /**
533          * <p>
534          * Defines a setter of a value that sets another field's value.
535          * </p>
536          * <p>
537          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
538          * </p>
539          *
540          * @param fieldDescription The field that holds the value to be set.
541          * @return An instrumentation that sets the field's value to the specified field's value.
542          */

543         Composable setsFieldValueOf(FieldDescription fieldDescription);
544
545         /**
546          * <p>
547          * Defines a setter of a value that sets another field's value.
548          * </p>
549          * <p>
550          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
551          * </p>
552          *
553          * @param fieldName The name of the field that is specified by the instrumented type.
554          * @return An instrumentation that sets the field's value to the specified field's value.
555          */

556         Composable setsFieldValueOf(String fieldName);
557
558         /**
559          * <p>
560          * Defines a setter of a value that sets another field's value.
561          * </p>
562          * <p>
563          * <b>Note</b>: If the instrumented method does not return {@code void}, a chained instrumentation must be supplied.
564          * </p>
565          *
566          * @param fieldNameExtractor A field name extractor for the field that is specified by the instrumented type.
567          * @return An instrumentation that sets the field's value to the specified field's value.
568          */

569         Composable setsFieldValueOf(FieldNameExtractor fieldNameExtractor);
570     }
571
572     /**
573      * A field accessor that can be configured to use a given assigner and runtime type use configuration.
574      */

575     public interface AssignerConfigurable extends PropertyConfigurable {
576
577         /**
578          * Returns a field accessor that is identical to this field accessor but uses the given assigner
579          * and runtime type use configuration.
580          *
581          * @param assigner The assigner to use.
582          * @param typing   Indicates if dynamic type castings should be attempted for incompatible assignments.
583          * @return This field accessor with the given assigner and runtime type use configuration.
584          */

585         PropertyConfigurable withAssigner(Assigner assigner, Assigner.Typing typing);
586     }
587
588     /**
589      * A field accessor that can be configured to locate a field in a specific manner.
590      */

591     public interface OwnerTypeLocatable extends AssignerConfigurable {
592
593         /**
594          * Determines that a field should only be considered when it was defined in a given type.
595          *
596          * @param type The type to be considered.
597          * @return This field accessor which will only considered fields that are defined in the given type.
598          */

599         AssignerConfigurable in(Class<?> type);
600
601         /**
602          * Determines that a field should only be considered when it was defined in a given type.
603          *
604          * @param typeDescription A description of the type to be considered.
605          * @return This field accessor which will only considered fields that are defined in the given type.
606          */

607         AssignerConfigurable in(TypeDescription typeDescription);
608
609         /**
610          * Determines that a field should only be considered when it was identified by a field locator that is
611          * produced by the given factory.
612          *
613          * @param fieldLocatorFactory A factory that will produce a field locator that will be used to find locate
614          *                            a field to be accessed.
615          * @return This field accessor which will only considered fields that are defined in the given type.
616          */

617         AssignerConfigurable in(FieldLocator.Factory fieldLocatorFactory);
618     }
619
620     /**
621      * A field accessor for an implicit property where a getter or setter property is inferred from the signature.
622      */

623     protected static class ForImplicitProperty extends FieldAccessor implements OwnerTypeLocatable {
624
625         /**
626          * Creates a field accessor for an implicit property.
627          *
628          * @param fieldLocation The field's location.
629          */

630         protected ForImplicitProperty(FieldLocation fieldLocation) {
631             this(fieldLocation, Assigner.DEFAULT, Assigner.Typing.STATIC);
632         }
633
634         /**
635          * Creates a field accessor for an implicit property.
636          *
637          * @param fieldLocation The field's location.
638          * @param assigner      The assigner to use.
639          * @param typing        The typing to use.
640          */

641         private ForImplicitProperty(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing) {
642             super(fieldLocation, assigner, typing);
643         }
644
645         /**
646          * {@inheritDoc}
647          */

648         public InstrumentedType prepare(InstrumentedType instrumentedType) {
649             return instrumentedType;
650         }
651
652         /**
653          * {@inheritDoc}
654          */

655         public ByteCodeAppender appender(Target implementationTarget) {
656             return new Appender(fieldLocation.prepare(implementationTarget.getInstrumentedType()));
657         }
658
659         /**
660          * {@inheritDoc}
661          */

662         public Composable setsArgumentAt(int index) {
663             if (index < 0) {
664                 throw new IllegalArgumentException("A parameter index cannot be negative: " + index);
665             }
666             return new ForSetter.OfParameterValue(fieldLocation,
667                     assigner,
668                     typing,
669                     ForSetter.TerminationHandler.RETURNING,
670                     index);
671         }
672
673         /**
674          * {@inheritDoc}
675          */

676         public Composable setsDefaultValue() {
677             return new ForSetter.OfDefaultValue(fieldLocation, assigner, typing, ForSetter.TerminationHandler.RETURNING);
678         }
679
680         /**
681          * {@inheritDoc}
682          */

683         public Composable setsValue(Object value) {
684             Class<?> type = value.getClass();
685             if (type == String.class) {
686                 return setsValue(new TextConstant((String) value), String.class);
687             } else if (type == Class.class) {
688                 return setsValue(ClassConstant.of(TypeDescription.ForLoadedType.of((Class<?>) value)), Class.class);
689             } else if (type == Boolean.class) {
690                 return setsValue(IntegerConstant.forValue((Boolean) value), boolean.class);
691             } else if (type == Byte.class) {
692                 return setsValue(IntegerConstant.forValue((Byte) value), byte.class);
693             } else if (type == Short.class) {
694                 return setsValue(IntegerConstant.forValue((Short) value), short.class);
695             } else if (type == Character.class) {
696                 return setsValue(IntegerConstant.forValue((Character) value), char.class);
697             } else if (type == Integer.class) {
698                 return setsValue(IntegerConstant.forValue((Integer) value), int.class);
699             } else if (type == Long.class) {
700                 return setsValue(LongConstant.forValue((Long) value), long.class);
701             } else if (type == Float.class) {
702                 return setsValue(FloatConstant.forValue((Float) value), float.class);
703             } else if (type == Double.class) {
704                 return setsValue(DoubleConstant.forValue((Double) value), double.class);
705             } else if (JavaType.METHOD_HANDLE.getTypeStub().isAssignableFrom(type)) {
706                 return setsValue(new JavaConstantValue(JavaConstant.MethodHandle.ofLoaded(value)), type);
707             } else if (JavaType.METHOD_TYPE.getTypeStub().represents(type)) {
708                 return setsValue(new JavaConstantValue(JavaConstant.MethodType.ofLoaded(value)), type);
709             } else {
710                 return setsReference(value);
711             }
712         }
713
714         /**
715          * {@inheritDoc}
716          */

717         public Composable setsValue(TypeDescription typeDescription) {
718             return setsValue(ClassConstant.of(typeDescription), Class.class);
719         }
720
721         /**
722          * {@inheritDoc}
723          */

724         public Composable setsValue(JavaConstant constant) {
725             return setsValue(new JavaConstantValue(constant), constant.getType().asGenericType());
726         }
727
728         /**
729          * {@inheritDoc}
730          */

731         public Composable setsValue(StackManipulation stackManipulation, Type type) {
732             return setsValue(stackManipulation, TypeDescription.Generic.Sort.describe(type));
733         }
734
735         /**
736          * {@inheritDoc}
737          */

738         public Composable setsValue(StackManipulation stackManipulation, TypeDescription.Generic typeDescription) {
739             return new ForSetter.OfConstantValue(fieldLocation,
740                     assigner,
741                     typing,
742                     ForSetter.TerminationHandler.RETURNING,
743                     typeDescription,
744                     stackManipulation);
745         }
746
747         /**
748          * {@inheritDoc}
749          */

750         public Composable setsReference(Object value) {
751             return setsReference(value, ForSetter.OfReferenceValue.PREFIX + "$" + RandomString.hashOf(value.hashCode()));
752         }
753
754         /**
755          * {@inheritDoc}
756          */

757         public Composable setsReference(Object value, String name) {
758             return new ForSetter.OfReferenceValue(fieldLocation,
759                     assigner,
760                     typing,
761                     ForSetter.TerminationHandler.RETURNING,
762                     value,
763                     name);
764         }
765
766         /**
767          * {@inheritDoc}
768          */

769         public Composable setsFieldValueOf(Field field) {
770             return setsFieldValueOf(new FieldDescription.ForLoadedField(field));
771         }
772
773         /**
774          * {@inheritDoc}
775          */

776         public Composable setsFieldValueOf(FieldDescription fieldDescription) {
777             return new ForSetter.OfFieldValue(fieldLocation,
778                     assigner,
779                     typing,
780                     ForSetter.TerminationHandler.RETURNING,
781                     new FieldLocation.Absolute(fieldDescription));
782         }
783
784         /**
785          * {@inheritDoc}
786          */

787         public Composable setsFieldValueOf(String fieldName) {
788             return setsFieldValueOf(new FieldNameExtractor.ForFixedValue(fieldName));
789         }
790
791         /**
792          * {@inheritDoc}
793          */

794         public Composable setsFieldValueOf(FieldNameExtractor fieldNameExtractor) {
795             return new ForSetter.OfFieldValue(fieldLocation,
796                     assigner,
797                     typing,
798                     ForSetter.TerminationHandler.RETURNING,
799                     new FieldLocation.Relative(fieldNameExtractor));
800         }
801
802         /**
803          * {@inheritDoc}
804          */

805         public PropertyConfigurable withAssigner(Assigner assigner, Assigner.Typing typing) {
806             return new ForImplicitProperty(fieldLocation, assigner, typing);
807         }
808
809         /**
810          * {@inheritDoc}
811          */

812         public AssignerConfigurable in(Class<?> type) {
813             return in(TypeDescription.ForLoadedType.of(type));
814         }
815
816         /**
817          * {@inheritDoc}
818          */

819         public AssignerConfigurable in(TypeDescription typeDescription) {
820             return in(new FieldLocator.ForExactType.Factory(typeDescription));
821         }
822
823         /**
824          * {@inheritDoc}
825          */

826         public AssignerConfigurable in(FieldLocator.Factory fieldLocatorFactory) {
827             return new ForImplicitProperty(fieldLocation.with(fieldLocatorFactory), assigner, typing);
828         }
829
830         /**
831          * An byte code appender for an field accessor implementation.
832          */

833         @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
834         protected class Appender implements ByteCodeAppender {
835
836             /**
837              * The field's location.
838              */

839             private final FieldLocation.Prepared fieldLocation;
840
841             /**
842              * Creates a new byte code appender for a field accessor implementation.
843              *
844              * @param fieldLocation    The field's location.
845              */

846             protected Appender(FieldLocation.Prepared fieldLocation) {
847                 this.fieldLocation = fieldLocation;
848             }
849
850             /**
851              * {@inheritDoc}
852              */

853             public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) {
854                 if (!instrumentedMethod.isMethod()) {
855                     throw new IllegalArgumentException(instrumentedMethod + " does not describe a field getter or setter");
856                 }
857                 FieldDescription fieldDescription = fieldLocation.resolve(instrumentedMethod);
858                 if (!fieldDescription.isStatic() && instrumentedMethod.isStatic()) {
859                     throw new IllegalStateException("Cannot set instance field " + fieldDescription + " from " + instrumentedMethod);
860                 }
861                 StackManipulation implementation, initialization = fieldDescription.isStatic()
862                         ? StackManipulation.Trivial.INSTANCE
863                         : MethodVariableAccess.loadThis();
864                 if (!instrumentedMethod.getReturnType().represents(void.class)) {
865                     implementation = new StackManipulation.Compound(
866                             initialization,
867                             FieldAccess.forField(fieldDescription).read(),
868                             assigner.assign(fieldDescription.getType(), instrumentedMethod.getReturnType(), typing),
869                             MethodReturn.of(instrumentedMethod.getReturnType())
870                     );
871                 } else if (instrumentedMethod.getReturnType().represents(void.class) && instrumentedMethod.getParameters().size() == 1) {
872                     if (fieldDescription.isFinal() && instrumentedMethod.isMethod()) {
873                         throw new IllegalStateException("Cannot set final field " + fieldDescription + " from " + instrumentedMethod);
874                     }
875                     implementation = new StackManipulation.Compound(
876                             initialization,
877                             MethodVariableAccess.load(instrumentedMethod.getParameters().get(0)),
878                             assigner.assign(instrumentedMethod.getParameters().get(0).getType(), fieldDescription.getType(), typing),
879                             FieldAccess.forField(fieldDescription).write(),
880                             MethodReturn.VOID
881                     );
882                 } else {
883                     throw new IllegalArgumentException("Method " + instrumentedMethod + " is no bean accessor");
884                 }
885                 if (!implementation.isValid()) {
886                     throw new IllegalStateException("Cannot set or get value of " + instrumentedMethod + " using " + fieldDescription);
887                 }
888                 return new Size(implementation.apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize());
889             }
890         }
891     }
892
893     /**
894      * A field accessor for a field setter.
895      *
896      * @param <T> The type of the value that is initialized per instrumented type.
897      */

898     @HashCodeAndEqualsPlugin.Enhance
899     protected abstract static class ForSetter<T> extends FieldAccessor implements Implementation.Composable {
900
901         /**
902          * The termination handler to apply.
903          */

904         private final TerminationHandler terminationHandler;
905
906         /**
907          * Creates a new field accessor for a setter instrumentation.
908          *
909          * @param fieldLocation      The field's location.
910          * @param assigner           The assigner to use.
911          * @param typing             Indicates if dynamic type castings should be attempted for incompatible assignments.
912          * @param terminationHandler The termination handler to apply.
913          */

914         protected ForSetter(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler) {
915             super(fieldLocation, assigner, typing);
916             this.terminationHandler = terminationHandler;
917         }
918
919         /**
920          * {@inheritDoc}
921          */

922         public ByteCodeAppender appender(Target implementationTarget) {
923             return new Appender(implementationTarget.getInstrumentedType(),
924                     initialize(implementationTarget.getInstrumentedType()),
925                     fieldLocation.prepare(implementationTarget.getInstrumentedType()));
926         }
927
928         /**
929          * Initializes a value to be used during method instrumentation.
930          *
931          * @param instrumentedType The instrumented type.
932          * @return The initialized value.
933          */

934         protected abstract T initialize(TypeDescription instrumentedType);
935
936         /**
937          * Resolves the stack manipulation to load the value being set.
938          *
939          * @param initialized        The method that was initialized for the instrumented type.
940          * @param fieldDescription   The field to set the value for.
941          * @param instrumentedType   The instrumented type.
942          * @param instrumentedMethod The instrumented method.
943          * @return The stack manipulation to apply.
944          */

945         protected abstract StackManipulation resolve(T initialized,
946                                                      FieldDescription fieldDescription,
947                                                      TypeDescription instrumentedType,
948                                                      MethodDescription instrumentedMethod);
949
950         /**
951          * A termination handler is responsible for handling a field accessor's return.
952          */

953         protected enum TerminationHandler {
954
955             /**
956              * Returns {@code void} or throws an exception if this is not the return type of the instrumented method.
957              */

958             RETURNING {
959                 @Override
960                 protected StackManipulation resolve(MethodDescription instrumentedMethod) {
961                     if (!instrumentedMethod.getReturnType().represents(void.class)) {
962                         throw new IllegalStateException("Cannot implement setter with return value for " + instrumentedMethod);
963                     }
964                     return MethodReturn.VOID;
965                 }
966             },
967
968             /**
969              * Does not return from the method at all.
970              */

971             NON_OPERATIONAL {
972                 @Override
973                 protected StackManipulation resolve(MethodDescription instrumentedMethod) {
974                     return StackManipulation.Trivial.INSTANCE;
975                 }
976             };
977
978             /**
979              * Resolves the return instruction.
980              *
981              * @param instrumentedMethod The instrumented method.
982              * @return An appropriate stack manipulation.
983              */

984             protected abstract StackManipulation resolve(MethodDescription instrumentedMethod);
985         }
986
987         /**
988          * A setter instrumentation for a parameter value.
989          */

990         @HashCodeAndEqualsPlugin.Enhance
991         protected static class OfParameterValue extends ForSetter<Void> {
992
993             /**
994              * The parameter's index.
995              */

996             private final int index;
997
998             /**
999              * Creates a new setter instrumentation for a parameter value.
1000              *
1001              * @param fieldLocation      The field's location.
1002              * @param assigner           The assigner to use.
1003              * @param typing             Indicates if dynamic type castings should be attempted for incompatible assignments.
1004              * @param terminationHandler The termination handler to apply.
1005              * @param index              The parameter's index.
1006              */

1007             protected OfParameterValue(FieldLocation fieldLocation,
1008                                        Assigner assigner,
1009                                        Assigner.Typing typing,
1010                                        TerminationHandler terminationHandler,
1011                                        int index) {
1012                 super(fieldLocation, assigner, typing, terminationHandler);
1013                 this.index = index;
1014             }
1015
1016             /**
1017              * {@inheritDoc}
1018              */

1019             public InstrumentedType prepare(InstrumentedType instrumentedType) {
1020                 return instrumentedType;
1021             }
1022
1023             /**
1024              * {@inheritDoc}
1025              */

1026             protected Void initialize(TypeDescription instrumentedType) {
1027                 return null;
1028             }
1029
1030             /**
1031              * {@inheritDoc}
1032              */

1033             protected StackManipulation resolve(Void unused,
1034                                                 FieldDescription fieldDescription,
1035                                                 TypeDescription instrumentedType,
1036                                                 MethodDescription instrumentedMethod) {
1037                 if (instrumentedMethod.getParameters().size() <= index) {
1038                     throw new IllegalStateException(instrumentedMethod + " does not define a parameter with index " + index);
1039                 } else {
1040                     return new StackManipulation.Compound(
1041                             MethodVariableAccess.load(instrumentedMethod.getParameters().get(index)),
1042                             assigner.assign(instrumentedMethod.getParameters().get(index).getType(), fieldDescription.getType(), typing)
1043                     );
1044                 }
1045             }
1046
1047             /**
1048              * {@inheritDoc}
1049              */

1050             public Implementation andThen(Implementation implementation) {
1051                 return new Compound(new OfParameterValue(fieldLocation,
1052                         assigner,
1053                         typing,
1054                         TerminationHandler.NON_OPERATIONAL,
1055                         index), implementation);
1056             }
1057
1058             /**
1059              * {@inheritDoc}
1060              */

1061             public Composable andThen(Composable implementation) {
1062                 return new Compound.Composable(new OfParameterValue(fieldLocation,
1063                         assigner,
1064                         typing,
1065                         TerminationHandler.NON_OPERATIONAL,
1066                         index), implementation);
1067             }
1068         }
1069
1070         /**
1071          * A setter instrumentation that sets a {@code null} or a primitive type's default value.
1072          */

1073         protected static class OfDefaultValue extends ForSetter<Void> {
1074
1075             /**
1076              * Creates an intrumentation that sets a field's default value.
1077              *
1078              * @param fieldLocation      The field's location.
1079              * @param assigner           The assigner to use.
1080              * @param typing             Indicates if dynamic type castings should be attempted for incompatible assignments.
1081              * @param terminationHandler The termination handler to apply.
1082              */

1083             protected OfDefaultValue(FieldLocation fieldLocation, Assigner assigner, Assigner.Typing typing, TerminationHandler terminationHandler) {
1084                 super(fieldLocation, assigner, typing, terminationHandler);
1085             }
1086
1087             /**
1088              * {@inheritDoc}
1089              */

1090             public InstrumentedType prepare(InstrumentedType instrumentedType) {
1091                 return instrumentedType;
1092             }
1093
1094             /**
1095              * {@inheritDoc}
1096              */

1097             protected Void initialize(TypeDescription instrumentedType) {
1098                 return null;
1099             }
1100
1101             /**
1102              * {@inheritDoc}
1103              */

1104             protected StackManipulation resolve(Void initialized,
1105                                                 FieldDescription fieldDescription,
1106                                                 TypeDescription instrumentedType,
1107                                                 MethodDescription instrumentedMethod) {
1108                 return DefaultValue.of(fieldDescription.getType());
1109             }
1110
1111             /**
1112              * {@inheritDoc}
1113              */

1114             public Implementation andThen(Implementation implementation) {
1115                 return new Compound(new OfDefaultValue(fieldLocation,
1116                         assigner,
1117                         typing,
1118                         TerminationHandler.NON_OPERATIONAL), implementation);
1119             }
1120
1121             /**
1122              * {@inheritDoc}
1123              */

1124             public Composable andThen(Composable implementation) {
1125                 return new Compound.Composable(new OfDefaultValue(fieldLocation,
1126                         assigner,
1127                         typing,
1128                         TerminationHandler.NON_OPERATIONAL), implementation);
1129             }
1130         }
1131
1132         /**
1133          * An instrumentation that sets a constant value to a field.
1134          */

1135         @HashCodeAndEqualsPlugin.Enhance
1136         protected static class OfConstantValue extends ForSetter<Void> {
1137
1138             /**
1139              * The value's type.
1140              */

1141             private final TypeDescription.Generic typeDescription;
1142
1143             /**
1144              * A stack manipulation to load the constant value.
1145              */

1146             private final StackManipulation stackManipulation;
1147
1148             /**
1149              * Creates a setter instrumentation for setting a constant value.
1150              *
1151              * @param fieldLocation      The field's location.
1152              * @param assigner           The assigner to use.
1153              * @param typing             Indicates if dynamic type castings should be attempted for incompatible assignments.
1154              * @param terminationHandler The termination handler to apply.
1155              * @param typeDescription    The value's type.
1156              * @param stackManipulation  A stack manipulation to load the constant value.
1157              */

1158             protected OfConstantValue(FieldLocation fieldLocation,
1159                                       Assigner assigner,
1160                                       Assigner.Typing typing,
1161                                       TerminationHandler terminationHandler,
1162                                       TypeDescription.Generic typeDescription,
1163                                       StackManipulation stackManipulation) {
1164                 super(fieldLocation, assigner, typing, terminationHandler);
1165                 this.typeDescription = typeDescription;
1166                 this.stackManipulation = stackManipulation;
1167             }
1168
1169             /**
1170              * {@inheritDoc}
1171              */

1172             public InstrumentedType prepare(InstrumentedType instrumentedType) {
1173                 return instrumentedType;
1174             }
1175
1176             /**
1177              * {@inheritDoc}
1178              */

1179             protected Void initialize(TypeDescription instrumentedType) {
1180                 return null;
1181             }
1182
1183             /**
1184              * {@inheritDoc}
1185              */

1186             protected StackManipulation resolve(Void unused,
1187                                                 FieldDescription fieldDescription,
1188                                                 TypeDescription instrumentedType,
1189                                                 MethodDescription instrumentedMethod) {
1190                 return new StackManipulation.Compound(stackManipulation, assigner.assign(typeDescription, fieldDescription.getType(), typing));
1191             }
1192
1193             /**
1194              * {@inheritDoc}
1195              */

1196             public Implementation andThen(Implementation implementation) {
1197                 return new Compound(new OfConstantValue(fieldLocation,
1198                         assigner,
1199                         typing,
1200                         TerminationHandler.NON_OPERATIONAL,
1201                         typeDescription,
1202                         stackManipulation), implementation);
1203             }
1204
1205             /**
1206              * {@inheritDoc}
1207              */

1208             public Composable andThen(Composable implementation) {
1209                 return new Compound.Composable(new OfConstantValue(fieldLocation,
1210                         assigner,
1211                         typing,
1212                         TerminationHandler.NON_OPERATIONAL,
1213                         typeDescription,
1214                         stackManipulation), implementation);
1215             }
1216         }
1217
1218         /**
1219          * An instrumentation that sets a field to a reference value that is stored in a static field of the instrumented type.
1220          */

1221         @HashCodeAndEqualsPlugin.Enhance
1222         protected static class OfReferenceValue extends ForSetter<FieldDescription.InDefinedShape> {
1223
1224             /**
1225              * The prefix used for implicitly named cached fields.
1226              */

1227             protected static final String PREFIX = "fixedFieldValue";
1228
1229             /**
1230              * The value to store.
1231              */

1232             private final Object value;
1233
1234             /**
1235              * The name of the field to store the reference in.
1236              */

1237             private final String name;
1238
1239             /**
1240              * Creates a setter instrumentation for setting a value stored in a static field of the instrumented type.
1241              *
1242              * @param fieldLocation      The field's location.
1243              * @param assigner           The assigner to use.
1244              * @param typing             Indicates if dynamic type castings should be attempted for incompatible assignments.
1245              * @param terminationHandler The termination handler to apply.
1246              * @param value              The value to store.
1247              * @param name               The name of the field to store the reference in.
1248              */

1249             protected OfReferenceValue(FieldLocation fieldLocation,
1250                                        Assigner assigner,
1251                                        Assigner.Typing typing,
1252                                        TerminationHandler terminationHandler,
1253                                        Object value,
1254                                        String name) {
1255                 super(fieldLocation, assigner, typing, terminationHandler);
1256                 this.value = value;
1257                 this.name = name;
1258             }
1259
1260             /**
1261              * {@inheritDoc}
1262              */

1263             public InstrumentedType prepare(InstrumentedType instrumentedType) {
1264                 return instrumentedType
1265                         .withField(new FieldDescription.Token(name, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, TypeDescription.ForLoadedType.of(value.getClass()).asGenericType()))
1266                         .withInitializer(new LoadedTypeInitializer.ForStaticField(name, value));
1267             }
1268
1269             /**
1270              * {@inheritDoc}
1271              */

1272             protected FieldDescription.InDefinedShape initialize(TypeDescription instrumentedType) {
1273                 return instrumentedType.getDeclaredFields().filter(named(name)).getOnly();
1274             }
1275
1276             /**
1277              * {@inheritDoc}
1278              */

1279             protected StackManipulation resolve(FieldDescription.InDefinedShape target,
1280                                                 FieldDescription fieldDescription,
1281                                                 TypeDescription instrumentedType,
1282                                                 MethodDescription instrumentedMethod) {
1283                 if (fieldDescription.isFinal() && instrumentedMethod.isMethod()) {
1284                     throw new IllegalArgumentException("Cannot set final field " + fieldDescription + " from " + instrumentedMethod);
1285                 }
1286                 return new StackManipulation.Compound(
1287                         FieldAccess.forField(target).read(),
1288                         assigner.assign(TypeDescription.ForLoadedType.of(value.getClass()).asGenericType(), fieldDescription.getType(), typing)
1289                 );
1290             }
1291
1292             /**
1293              * {@inheritDoc}
1294              */

1295             public Implementation andThen(Implementation implementation) {
1296                 return new Compound(new OfReferenceValue(fieldLocation,
1297                         assigner,
1298                         typing,
1299                         TerminationHandler.NON_OPERATIONAL,
1300                         value,
1301                         name), implementation);
1302             }
1303
1304             /**
1305              * {@inheritDoc}
1306              */

1307             public Composable andThen(Composable implementation) {
1308                 return new Compound.Composable(new OfReferenceValue(fieldLocation,
1309                         assigner,
1310                         typing,
1311                         TerminationHandler.NON_OPERATIONAL,
1312                         value,
1313                         name), implementation);
1314             }
1315         }
1316
1317         /**
1318          * A setter that reads a value of another field and sets this value.
1319          */

1320         @HashCodeAndEqualsPlugin.Enhance
1321         protected static class OfFieldValue extends ForSetter<FieldLocation.Prepared> {
1322
1323             /**
1324              * The target field locator.
1325              */

1326             private final FieldLocation target;
1327
1328             /**
1329              * Creates a setter that sets another field value.
1330              *
1331              * @param fieldLocation      The field's location.
1332              * @param assigner           The assigner to use.
1333              * @param typing             Indicates if dynamic type castings should be attempted for incompatible assignments.
1334              * @param terminationHandler The termination handler to apply.
1335              * @param target             The target field locator.
1336              */

1337             protected OfFieldValue(FieldLocation fieldLocation,
1338                                    Assigner assigner,
1339                                    Assigner.Typing typing,
1340                                    TerminationHandler terminationHandler,
1341                                    FieldLocation target) {
1342                 super(fieldLocation, assigner, typing, terminationHandler);
1343                 this.target = target;
1344             }
1345
1346             /**
1347              * {@inheritDoc}
1348              */

1349             public InstrumentedType prepare(InstrumentedType instrumentedType) {
1350                 return instrumentedType;
1351             }
1352
1353             /**
1354              * {@inheritDoc}
1355              */

1356             protected FieldLocation.Prepared initialize(TypeDescription instrumentedType) {
1357                 return target.prepare(instrumentedType);
1358             }
1359
1360             /**
1361              * {@inheritDoc}
1362              */

1363             protected StackManipulation resolve(FieldLocation.Prepared target,
1364                                                 FieldDescription fieldDescription,
1365                                                 TypeDescription instrumentedType,
1366                                                 MethodDescription instrumentedMethod) {
1367                 FieldDescription resolved = target.resolve(instrumentedMethod);
1368                 if (!resolved.isStatic() && instrumentedMethod.isStatic()) {
1369                     throw new IllegalStateException("Cannot set instance field " + fieldDescription + " from " + instrumentedMethod);
1370                 }
1371                 return new StackManipulation.Compound(
1372                         resolved.isStatic()
1373                                 ? StackManipulation.Trivial.INSTANCE
1374                                 : MethodVariableAccess.loadThis(),
1375                         FieldAccess.forField(resolved).read(),
1376                         assigner.assign(resolved.getType(), fieldDescription.getType(), typing)
1377                 );
1378             }
1379
1380             /**
1381              * {@inheritDoc}
1382              */

1383             public Implementation andThen(Implementation implementation) {
1384                 return new Compound(new OfFieldValue(fieldLocation,
1385                         assigner,
1386                         typing,
1387                         TerminationHandler.NON_OPERATIONAL,
1388                         target), implementation);
1389             }
1390
1391             /**
1392              * {@inheritDoc}
1393              */

1394             public Composable andThen(Composable implementation) {
1395                 return new Compound.Composable(new OfFieldValue(fieldLocation,
1396                         assigner,
1397                         typing,
1398                         TerminationHandler.NON_OPERATIONAL,
1399                         target), implementation);
1400             }
1401         }
1402
1403         /**
1404          * An appender to implement a field setter.
1405          */

1406         @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
1407         protected class Appender implements ByteCodeAppender {
1408
1409             /**
1410              * The instrumented type.
1411              */

1412             private final TypeDescription instrumentedType;
1413
1414             /**
1415              * The initialized value.
1416              */

1417             private final T initialized;
1418
1419             /**
1420              * The set field's prepared location.
1421              */

1422             private final FieldLocation.Prepared fieldLocation;
1423
1424             /**
1425              * Creates a new appender for a field setter.
1426              *
1427              * @param instrumentedType The instrumented type.
1428              * @param initialized      The initialized value.
1429              * @param fieldLocation    The set field's prepared location.
1430              */

1431             protected Appender(TypeDescription instrumentedType, T initialized, FieldLocation.Prepared fieldLocation) {
1432                 this.instrumentedType = instrumentedType;
1433                 this.initialized = initialized;
1434                 this.fieldLocation = fieldLocation;
1435             }
1436
1437             /**
1438              * {@inheritDoc}
1439              */

1440             public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
1441                 FieldDescription fieldDescription = fieldLocation.resolve(instrumentedMethod);
1442                 if (!fieldDescription.isStatic() && instrumentedMethod.isStatic()) {
1443                     throw new IllegalStateException("Cannot set instance field " + fieldDescription + " from " + instrumentedMethod);
1444                 } else if (fieldDescription.isFinal() && instrumentedMethod.isMethod()) {
1445                     throw new IllegalStateException("Cannot set final field " + fieldDescription + " from " + instrumentedMethod);
1446                 }
1447                 StackManipulation stackManipulation = resolve(initialized, fieldDescription, instrumentedType, instrumentedMethod);
1448                 if (!stackManipulation.isValid()) {
1449                     throw new IllegalStateException("Set value cannot be assigned to " + fieldDescription);
1450                 }
1451                 return new Size(new StackManipulation.Compound(
1452                         instrumentedMethod.isStatic()
1453                                 ? StackManipulation.Trivial.INSTANCE
1454                                 : MethodVariableAccess.loadThis(),
1455                         stackManipulation,
1456                         FieldAccess.forField(fieldDescription).write(),
1457                         terminationHandler.resolve(instrumentedMethod)
1458                 ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize());
1459             }
1460         }
1461     }
1462 }
1463