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