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.bytecode.member;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.enumeration.EnumerationDescription;
20 import net.bytebuddy.description.field.FieldDescription;
21 import net.bytebuddy.description.field.FieldList;
22 import net.bytebuddy.description.type.TypeDefinition;
23 import net.bytebuddy.implementation.Implementation;
24 import net.bytebuddy.implementation.bytecode.StackManipulation;
25 import net.bytebuddy.implementation.bytecode.StackSize;
26 import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
27 import net.bytebuddy.jar.asm.MethodVisitor;
28 import net.bytebuddy.jar.asm.Opcodes;
29
30 import static net.bytebuddy.matcher.ElementMatchers.named;
31
32 /**
33  * An access representation to a given field.
34  */

35 public enum FieldAccess {
36
37     /**
38      * The representation of field access to a static field.
39      */

40     STATIC(Opcodes.PUTSTATIC, Opcodes.GETSTATIC, StackSize.ZERO),
41
42     /**
43      * The representation of field access to an instance field.
44      */

45     INSTANCE(Opcodes.PUTFIELD, Opcodes.GETFIELD, StackSize.SINGLE);
46
47     /**
48      * The opcode for setting a field value.
49      */

50     private final int putterOpcode;
51
52     /**
53      * The opcode for getting a field value.
54      */

55     private final int getterOpcode;
56
57     /**
58      * The amount of operand slots this field access operation consumes when it is applied before eventually
59      * adding new values onto the operand stack.
60      */

61     private final int targetSizeChange;
62
63     /**
64      * Creates a new field access.
65      *
66      * @param putterOpcode     The opcode for setting a field value.
67      * @param getterOpcode     The opcode for getting a field value.
68      * @param targetSizeChange The amount of operand slots this field access operation consumes when it is applied
69      *                         before eventually adding new values onto the operand stack.
70      */

71     FieldAccess(int putterOpcode, int getterOpcode, StackSize targetSizeChange) {
72         this.putterOpcode = putterOpcode;
73         this.getterOpcode = getterOpcode;
74         this.targetSizeChange = targetSizeChange.getSize();
75     }
76
77     /**
78      * Creates an accessor to read an enumeration value.
79      *
80      * @param enumerationDescription The description of the enumeration.
81      * @return A stack manipulation for reading the enumeration.
82      */

83     public static StackManipulation forEnumeration(EnumerationDescription enumerationDescription) {
84         FieldList<FieldDescription.InDefinedShape> fieldList = enumerationDescription.getEnumerationType()
85                 .getDeclaredFields()
86                 .filter(named(enumerationDescription.getValue()));
87         return fieldList.size() != 1 || !fieldList.getOnly().isStatic() || !fieldList.getOnly().isPublic() || !fieldList.getOnly().isEnum()
88                 ? StackManipulation.Illegal.INSTANCE
89                 : STATIC.new AccessDispatcher(fieldList.getOnly()).read();
90     }
91
92     /**
93      * Creates a field access representation for a given field.
94      *
95      * @param fieldDescription The field to be accessed.
96      * @return A field access definition for the given field.
97      */

98     public static Defined forField(FieldDescription.InDefinedShape fieldDescription) {
99         return fieldDescription.isStatic()
100                 ? STATIC.new AccessDispatcher(fieldDescription)
101                 : INSTANCE.new AccessDispatcher(fieldDescription);
102     }
103
104     /**
105      * Creates a field access representation for a given field. If the field's return type derives from its declared shape, the value
106      * is additionally casted to the generically resolved field.
107      *
108      * @param fieldDescription The field to be accessed.
109      * @return A field access definition for the given field.
110      */

111     public static Defined forField(FieldDescription fieldDescription) {
112         FieldDescription.InDefinedShape declaredField = fieldDescription.asDefined();
113         return fieldDescription.getType().asErasure().equals(declaredField.getType().asErasure())
114                 ? forField(declaredField)
115                 : OfGenericField.of(fieldDescription, forField(declaredField));
116     }
117
118     /**
119      * Representation of a field access for which a getter and a setter can be created.
120      */

121     public interface Defined {
122
123         /**
124          * Creates a getter representation for a given field.
125          *
126          * @return A stack manipulation representing the retrieval of a field value.
127          */

128         StackManipulation read();
129
130         /**
131          * Creates a setter representation for a given field.
132          *
133          * @return A stack manipulation representing the setting of a field value.
134          */

135         StackManipulation write();
136     }
137
138     /**
139      * A dispatcher for implementing a generic read or write access on a field.
140      */

141     @HashCodeAndEqualsPlugin.Enhance
142     protected static class OfGenericField implements Defined {
143
144         /**
145          * The resolved generic field type.
146          */

147         private final TypeDefinition targetType;
148
149         /**
150          * An accessor for the field in its defined shape.
151          */

152         private final Defined defined;
153
154         /**
155          * Creates a new dispatcher for a generic field.
156          *
157          * @param targetType The resolved generic field type.
158          * @param defined    An accessor for the field in its defined shape.
159          */

160         protected OfGenericField(TypeDefinition targetType, Defined defined) {
161             this.targetType = targetType;
162             this.defined = defined;
163         }
164
165         /**
166          * Creates a generic access dispatcher for a given field.
167          *
168          * @param fieldDescription The field that is being accessed.
169          * @param fieldAccess      A field accessor for the field in its defined shape.
170          * @return A field access dispatcher for the given field.
171          */

172         protected static Defined of(FieldDescription fieldDescription, Defined fieldAccess) {
173             return new OfGenericField(fieldDescription.getType(), fieldAccess);
174         }
175
176         /**
177          * {@inheritDoc}
178          */

179         public StackManipulation read() {
180             return new StackManipulation.Compound(defined.read(), TypeCasting.to(targetType));
181         }
182
183         /**
184          * {@inheritDoc}
185          */

186         public StackManipulation write() {
187             return defined.write();
188         }
189     }
190
191     /**
192      * A dispatcher for implementing a non-generic read or write access on a field.
193      */

194     @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
195     protected class AccessDispatcher implements Defined {
196
197         /**
198          * A description of the accessed field.
199          */

200         private final FieldDescription.InDefinedShape fieldDescription;
201
202         /**
203          * Creates a new access dispatcher.
204          *
205          * @param fieldDescription A description of the accessed field.
206          */

207         protected AccessDispatcher(FieldDescription.InDefinedShape fieldDescription) {
208             this.fieldDescription = fieldDescription;
209         }
210
211         /**
212          * {@inheritDoc}
213          */

214         public StackManipulation read() {
215             return new FieldGetInstruction();
216         }
217
218         /**
219          * {@inheritDoc}
220          */

221         public StackManipulation write() {
222             return new FieldPutInstruction();
223         }
224
225         /**
226          * An abstract base implementation for accessing a field value.
227          */

228         private abstract class AbstractFieldInstruction implements StackManipulation {
229
230             /**
231              * {@inheritDoc}
232              */

233             public boolean isValid() {
234                 return true;
235             }
236
237             /**
238              * {@inheritDoc}
239              */

240             public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
241                 methodVisitor.visitFieldInsn(getOpcode(),
242                         fieldDescription.getDeclaringType().getInternalName(),
243                         fieldDescription.getInternalName(),
244                         fieldDescription.getDescriptor());
245                 return resolveSize(fieldDescription.getType().getStackSize());
246             }
247
248             /**
249              * Returns the opcode for implementing the field access.
250              *
251              * @return The opcode for implementing the field access.
252              */

253             protected abstract int getOpcode();
254
255             /**
256              * Resolves the actual size of this field access operation.
257              *
258              * @param fieldSize The size of the accessed field.
259              * @return The size of the field access operation based on the field's size.
260              */

261             protected abstract Size resolveSize(StackSize fieldSize);
262         }
263
264         /**
265          * A reading field access operation.
266          */

267         @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
268         protected class FieldGetInstruction extends AbstractFieldInstruction {
269
270             @Override
271             protected int getOpcode() {
272                 return getterOpcode;
273             }
274
275             @Override
276             protected Size resolveSize(StackSize fieldSize) {
277                 int sizeChange = fieldSize.getSize() - targetSizeChange;
278                 return new Size(sizeChange, sizeChange);
279             }
280         }
281
282         /**
283          * A writing field access operation.
284          */

285         @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
286         protected class FieldPutInstruction extends AbstractFieldInstruction {
287
288             @Override
289             protected int getOpcode() {
290                 return putterOpcode;
291             }
292
293             @Override
294             protected Size resolveSize(StackSize fieldSize) {
295                 return new Size(-1 * (fieldSize.getSize() + targetSizeChange), 0);
296             }
297         }
298     }
299 }
300