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