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.description.type.TypeDefinition;
19 import net.bytebuddy.description.type.TypeDescription;
20 import net.bytebuddy.implementation.Implementation;
21 import net.bytebuddy.implementation.bytecode.StackManipulation;
22 import net.bytebuddy.implementation.bytecode.StackSize;
23 import net.bytebuddy.implementation.bytecode.assign.Assigner;
24 import net.bytebuddy.jar.asm.MethodVisitor;
25 import net.bytebuddy.jar.asm.Opcodes;
26
27 /**
28 * This delegate is responsible for boxing a primitive types to their wrapper equivalents.
29 */
30 public enum PrimitiveBoxingDelegate {
31
32 /**
33 * The boxing delegate for {@code boolean} values.
34 */
35 BOOLEAN(Boolean.class, StackSize.ZERO, "valueOf", "(Z)Ljava/lang/Boolean;"),
36
37 /**
38 * The boxing delegate for {@code byte} values.
39 */
40 BYTE(Byte.class, StackSize.ZERO, "valueOf", "(B)Ljava/lang/Byte;"),
41
42 /**
43 * The boxing delegate for {@code short} values.
44 */
45 SHORT(Short.class, StackSize.ZERO, "valueOf", "(S)Ljava/lang/Short;"),
46
47 /**
48 * The boxing delegate for {@code char} values.
49 */
50 CHARACTER(Character.class, StackSize.ZERO, "valueOf", "(C)Ljava/lang/Character;"),
51
52 /**
53 * The boxing delegate for {@code int} values.
54 */
55 INTEGER(Integer.class, StackSize.ZERO, "valueOf", "(I)Ljava/lang/Integer;"),
56
57 /**
58 * The boxing delegate for {@code long} values.
59 */
60 LONG(Long.class, StackSize.SINGLE, "valueOf", "(J)Ljava/lang/Long;"),
61
62 /**
63 * The boxing delegate for {@code float} values.
64 */
65 FLOAT(Float.class, StackSize.ZERO, "valueOf", "(F)Ljava/lang/Float;"),
66
67 /**
68 * The boxing delegate for {@code double} values.
69 */
70 DOUBLE(Double.class, StackSize.SINGLE, "valueOf", "(D)Ljava/lang/Double;");
71
72 /**
73 * A description of a wrapper type.
74 */
75 private final TypeDescription wrapperType;
76
77 /**
78 * The size decrease after a primitive type was wrapped.
79 */
80 private final StackManipulation.Size size;
81
82 /**
83 * The name of the method for boxing a primitive value as its wrapper type.
84 */
85 private final String boxingMethodName;
86
87 /**
88 * The descriptor of the method for boxing a primitive value as its wrapper type.
89 */
90 private final String boxingMethodDescriptor;
91
92 /**
93 * Creates a new primitive boxing delegate.
94 *
95 * @param wrapperType A description of a wrapper type.
96 * @param sizeDifference The size difference between a primitive type and its wrapper type.
97 * @param boxingMethodName The name of the method for boxing a primitive value as its wrapper type.
98 * @param boxingMethodDescriptor The descriptor of the method for boxing a primitive value as its wrapper type.
99 */
100 PrimitiveBoxingDelegate(Class<?> wrapperType,
101 StackSize sizeDifference,
102 String boxingMethodName,
103 String boxingMethodDescriptor) {
104 this.wrapperType = TypeDescription.ForLoadedType.of(wrapperType);
105 this.size = sizeDifference.toDecreasingSize();
106 this.boxingMethodName = boxingMethodName;
107 this.boxingMethodDescriptor = boxingMethodDescriptor;
108 }
109
110 /**
111 * Locates a boxing delegate for a given primitive type.
112 *
113 * @param typeDefinition A non-void primitive type.
114 * @return A delegate capable of boxing the given primitive type.
115 */
116 public static PrimitiveBoxingDelegate forPrimitive(TypeDefinition typeDefinition) {
117 if (typeDefinition.represents(boolean.class)) {
118 return BOOLEAN;
119 } else if (typeDefinition.represents(byte.class)) {
120 return BYTE;
121 } else if (typeDefinition.represents(short.class)) {
122 return SHORT;
123 } else if (typeDefinition.represents(char.class)) {
124 return CHARACTER;
125 } else if (typeDefinition.represents(int.class)) {
126 return INTEGER;
127 } else if (typeDefinition.represents(long.class)) {
128 return LONG;
129 } else if (typeDefinition.represents(float.class)) {
130 return FLOAT;
131 } else if (typeDefinition.represents(double.class)) {
132 return DOUBLE;
133 } else {
134 throw new IllegalArgumentException("Not a non-void, primitive type: " + typeDefinition);
135 }
136 }
137
138 /**
139 * Creates a stack manipulation that boxes the represented primitive type and applies a chained assignment
140 * to the result of this boxing operation.
141 *
142 * @param target The type that is target of the assignment operation.
143 * @param chainedAssigner The assigner that is to be used to perform the chained assignment.
144 * @param typing Determines if an assignment to an incompatible type should be enforced by a casting.
145 * @return A stack manipulation that represents the described assignment operation.
146 */
147 public StackManipulation assignBoxedTo(TypeDescription.Generic target, Assigner chainedAssigner, Assigner.Typing typing) {
148 return new BoxingStackManipulation(chainedAssigner.assign(wrapperType.asGenericType(), target, typing));
149 }
150
151 /**
152 * A stack manipulation for boxing a primitive type into its wrapper type.
153 */
154 private class BoxingStackManipulation implements StackManipulation {
155
156 /**
157 * A stack manipulation that is applied after the boxing of the top-most value on the operand stack.
158 */
159 private final StackManipulation stackManipulation;
160
161 /**
162 * Creates a new boxing stack manipulation.
163 *
164 * @param stackManipulation A stack manipulation that is applied after the boxing of the top-most value on
165 * the operand stack.
166 */
167 public BoxingStackManipulation(StackManipulation stackManipulation) {
168 this.stackManipulation = stackManipulation;
169 }
170
171 /**
172 * {@inheritDoc}
173 */
174 public boolean isValid() {
175 return stackManipulation.isValid();
176 }
177
178 /**
179 * {@inheritDoc}
180 */
181 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
182 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
183 wrapperType.getInternalName(),
184 boxingMethodName,
185 boxingMethodDescriptor,
186 false);
187 return size.aggregate(stackManipulation.apply(methodVisitor, implementationContext));
188 }
189 }
190 }
191