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