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.assign.primitive;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.type.TypeDefinition;
20 import net.bytebuddy.description.type.TypeDescription;
21 import net.bytebuddy.implementation.Implementation;
22 import net.bytebuddy.implementation.bytecode.StackManipulation;
23 import net.bytebuddy.implementation.bytecode.StackSize;
24 import net.bytebuddy.implementation.bytecode.assign.Assigner;
25 import net.bytebuddy.jar.asm.MethodVisitor;
26 import net.bytebuddy.jar.asm.Opcodes;
27
28 /**
29 * This delegate is responsible for unboxing a wrapper type to their primitive equivalents.
30 */
31 public enum PrimitiveUnboxingDelegate implements StackManipulation {
32
33 /**
34 * The unboxing delegate for {@code Boolean} types.
35 */
36 BOOLEAN(Boolean.class, boolean.class, StackSize.ZERO, "booleanValue", "()Z"),
37
38 /**
39 * The unboxing delegate for {@code Byte} types.
40 */
41 BYTE(Byte.class, byte.class, StackSize.ZERO, "byteValue", "()B"),
42
43 /**
44 * The unboxing delegate for {@code Short} types.
45 */
46 SHORT(Short.class, short.class, StackSize.ZERO, "shortValue", "()S"),
47
48 /**
49 * The unboxing delegate for {@code Character} types.
50 */
51 CHARACTER(Character.class, char.class, StackSize.ZERO, "charValue", "()C"),
52
53 /**
54 * The unboxing delegate for {@code Integer} types.
55 */
56 INTEGER(Integer.class, int.class, StackSize.ZERO, "intValue", "()I"),
57
58 /**
59 * The unboxing delegate for {@code Long} types.
60 */
61 LONG(Long.class, long.class, StackSize.SINGLE, "longValue", "()J"),
62
63 /**
64 * The unboxing delegate for {@code Float} types.
65 */
66 FLOAT(Float.class, float.class, StackSize.ZERO, "floatValue", "()F"),
67
68 /**
69 * The unboxing delegate for {@code Double} types.
70 */
71 DOUBLE(Double.class, double.class, StackSize.SINGLE, "doubleValue", "()D");
72
73 /**
74 * The wrapper type of the represented primitive type.
75 */
76 private final TypeDescription wrapperType;
77
78 /**
79 * The represented primitive type.
80 */
81 private final TypeDescription primitiveType;
82
83 /**
84 * The size increase after a wrapper type was unwrapped.
85 */
86 private final Size size;
87
88 /**
89 * The name of the method for unboxing a wrapper value to its primitive value.
90 */
91 private final String unboxingMethodName;
92
93 /**
94 * The descriptor of the method for unboxing a wrapper value to its primitive value.
95 */
96 private final String unboxingMethodDescriptor;
97
98 /**
99 * Creates a new primitive unboxing delegate.
100 *
101 * @param wrapperType The wrapper type of the represented primitive type.
102 * @param primitiveType The represented primitive type.
103 * @param sizeDifference The size difference between the wrapper type and its primitive value.
104 * @param unboxingMethodName The name of the method for unboxing a wrapper value to its primitive value.
105 * @param unboxingMethodDescriptor The descriptor of the method for unboxing a wrapper value to its primitive value.
106 */
107 PrimitiveUnboxingDelegate(Class<?> wrapperType,
108 Class<?> primitiveType,
109 StackSize sizeDifference,
110 String unboxingMethodName,
111 String unboxingMethodDescriptor) {
112 this.size = sizeDifference.toIncreasingSize();
113 this.wrapperType = TypeDescription.ForLoadedType.of(wrapperType);
114 this.primitiveType = TypeDescription.ForLoadedType.of(primitiveType);
115 this.unboxingMethodName = unboxingMethodName;
116 this.unboxingMethodDescriptor = unboxingMethodDescriptor;
117 }
118
119 /**
120 * Locates a primitive unboxing delegate for a given primitive type.
121 *
122 * @param typeDefinition A description of the primitive type.
123 * @return A corresponding primitive unboxing delegate.
124 */
125 public static PrimitiveUnboxingDelegate forPrimitive(TypeDefinition typeDefinition) {
126 if (typeDefinition.represents(boolean.class)) {
127 return BOOLEAN;
128 } else if (typeDefinition.represents(byte.class)) {
129 return BYTE;
130 } else if (typeDefinition.represents(short.class)) {
131 return SHORT;
132 } else if (typeDefinition.represents(char.class)) {
133 return CHARACTER;
134 } else if (typeDefinition.represents(int.class)) {
135 return INTEGER;
136 } else if (typeDefinition.represents(long.class)) {
137 return LONG;
138 } else if (typeDefinition.represents(float.class)) {
139 return FLOAT;
140 } else if (typeDefinition.represents(double.class)) {
141 return DOUBLE;
142 } else {
143 throw new IllegalArgumentException("Expected non-void primitive type instead of " + typeDefinition);
144 }
145 }
146
147 /**
148 * Creates an unboxing responsible that is capable of unboxing a wrapper type.
149 * <ol>
150 * <li>If the reference type represents a wrapper type, the wrapper type will simply be unboxed.</li>
151 * <li>If the reference type does not represent a wrapper type, the wrapper type will be inferred by the primitive target
152 * type that is later given to the
153 * {@link net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate.UnboxingResponsible}
154 * in order to then check if the given type is assignable to the inferred wrapper type.</li>
155 * </ol>
156 *
157 * @param typeDefinition A non-primitive type.
158 * @return An unboxing responsible capable of performing an unboxing operation while considering a further assignment
159 * of the unboxed value.
160 */
161 public static UnboxingResponsible forReferenceType(TypeDefinition typeDefinition) {
162 if (typeDefinition.isPrimitive()) {
163 throw new IllegalArgumentException("Expected reference type instead of " + typeDefinition);
164 } else if (typeDefinition.represents(Boolean.class)) {
165 return ExplicitlyTypedUnboxingResponsible.BOOLEAN;
166 } else if (typeDefinition.represents(Byte.class)) {
167 return ExplicitlyTypedUnboxingResponsible.BYTE;
168 } else if (typeDefinition.represents(Short.class)) {
169 return ExplicitlyTypedUnboxingResponsible.SHORT;
170 } else if (typeDefinition.represents(Character.class)) {
171 return ExplicitlyTypedUnboxingResponsible.CHARACTER;
172 } else if (typeDefinition.represents(Integer.class)) {
173 return ExplicitlyTypedUnboxingResponsible.INTEGER;
174 } else if (typeDefinition.represents(Long.class)) {
175 return ExplicitlyTypedUnboxingResponsible.LONG;
176 } else if (typeDefinition.represents(Float.class)) {
177 return ExplicitlyTypedUnboxingResponsible.FLOAT;
178 } else if (typeDefinition.represents(Double.class)) {
179 return ExplicitlyTypedUnboxingResponsible.DOUBLE;
180 } else {
181 return new ImplicitlyTypedUnboxingResponsible(typeDefinition.asGenericType());
182 }
183 }
184
185 /**
186 * Returns the wrapper type that this unboxing delegate represents.
187 *
188 * @return A generic version of this delegate's wrapper type.
189 */
190 protected TypeDescription.Generic getWrapperType() {
191 return wrapperType.asGenericType();
192 }
193
194 /**
195 * {@inheritDoc}
196 */
197 public boolean isValid() {
198 return true;
199 }
200
201 /**
202 * {@inheritDoc}
203 */
204 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
205 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
206 wrapperType.asErasure().getInternalName(),
207 unboxingMethodName,
208 unboxingMethodDescriptor,
209 false);
210 return size;
211 }
212
213 /**
214 * An explicitly types unboxing responsible is applied for directly unboxing a wrapper type.
215 */
216 protected enum ExplicitlyTypedUnboxingResponsible implements UnboxingResponsible {
217
218 /**
219 * An unboxing responsible for unboxing a {@link java.lang.Boolean} type.
220 */
221 BOOLEAN(PrimitiveUnboxingDelegate.BOOLEAN),
222
223 /**
224 * An unboxing responsible for unboxing a {@link java.lang.Byte} type.
225 */
226 BYTE(PrimitiveUnboxingDelegate.BYTE),
227
228 /**
229 * An unboxing responsible for unboxing a {@link java.lang.Short} type.
230 */
231 SHORT(PrimitiveUnboxingDelegate.SHORT),
232
233 /**
234 * An unboxing responsible for unboxing a {@link java.lang.Character} type.
235 */
236 CHARACTER(PrimitiveUnboxingDelegate.CHARACTER),
237
238 /**
239 * An unboxing responsible for unboxing a {@link java.lang.Integer} type.
240 */
241 INTEGER(PrimitiveUnboxingDelegate.INTEGER),
242
243 /**
244 * An unboxing responsible for unboxing a {@link java.lang.Long} type.
245 */
246 LONG(PrimitiveUnboxingDelegate.LONG),
247
248 /**
249 * An unboxing responsible for unboxing a {@link java.lang.Float} type.
250 */
251 FLOAT(PrimitiveUnboxingDelegate.FLOAT),
252
253 /**
254 * An unboxing responsible for unboxing a {@link java.lang.Double} type.
255 */
256 DOUBLE(PrimitiveUnboxingDelegate.DOUBLE);
257
258 /**
259 * The primitive unboxing delegate for handling the given wrapper type.
260 */
261 private final PrimitiveUnboxingDelegate primitiveUnboxingDelegate;
262
263 /**
264 * Creates a new explicitly typed unboxing responsible.
265 *
266 * @param primitiveUnboxingDelegate The primitive unboxing delegate for handling the given wrapper type.
267 */
268 ExplicitlyTypedUnboxingResponsible(PrimitiveUnboxingDelegate primitiveUnboxingDelegate) {
269 this.primitiveUnboxingDelegate = primitiveUnboxingDelegate;
270 }
271
272 /**
273 * {@inheritDoc}
274 */
275 public StackManipulation assignUnboxedTo(TypeDescription.Generic targetType, Assigner assigner, Assigner.Typing typing) {
276 return new Compound(
277 primitiveUnboxingDelegate,
278 PrimitiveWideningDelegate.forPrimitive(primitiveUnboxingDelegate.primitiveType).widenTo(targetType));
279 }
280 }
281
282 /**
283 * Implementations represent an unboxing delegate that is able to perform the unboxing operation.
284 */
285 public interface UnboxingResponsible {
286
287 /**
288 * Attempts to unbox the represented type in order to assign the unboxed value to the given target type
289 * while using the assigner that is provided by the method call.
290 *
291 * @param target The type that is the desired outcome of the assignment.
292 * @param assigner The assigner used to assign the unboxed type to the target type.
293 * @param typing Determines if a type-casting should be attempted for incompatible types.
294 * @return A stack manipulation representing this assignment if such an assignment is possible. An illegal
295 * assignment otherwise.
296 */
297 StackManipulation assignUnboxedTo(TypeDescription.Generic target, Assigner assigner, Assigner.Typing typing);
298 }
299
300 /**
301 * An unboxing responsible for an implicitly typed value. This implementation is applied for source types that
302 * were not found to be of a given wrapper type. Instead, this unboxing responsible tries to assign the
303 * source type to the primitive target type's wrapper type before performing an unboxing operation.
304 */
305 @HashCodeAndEqualsPlugin.Enhance
306 protected static class ImplicitlyTypedUnboxingResponsible implements UnboxingResponsible {
307
308 /**
309 * The original type which should be unboxed but is not of any known wrapper type.
310 */
311 private final TypeDescription.Generic originalType;
312
313 /**
314 * Creates a new implicitly typed unboxing responsible.
315 *
316 * @param originalType The original type which should be unboxed but is not of any known wrapper type.
317 */
318 protected ImplicitlyTypedUnboxingResponsible(TypeDescription.Generic originalType) {
319 this.originalType = originalType;
320 }
321
322 /**
323 * {@inheritDoc}
324 */
325 public StackManipulation assignUnboxedTo(TypeDescription.Generic target, Assigner assigner, Assigner.Typing typing) {
326 PrimitiveUnboxingDelegate primitiveUnboxingDelegate = PrimitiveUnboxingDelegate.forPrimitive(target);
327 return new Compound(
328 assigner.assign(originalType, primitiveUnboxingDelegate.getWrapperType(), typing),
329 primitiveUnboxingDelegate);
330 }
331 }
332 }
333