1
2
3
4
5
6 package mockit.internal.util;
7
8 import static java.util.Collections.emptyIterator;
9 import static java.util.Collections.emptyList;
10 import static java.util.Collections.emptyMap;
11 import static java.util.Collections.emptySet;
12 import static java.util.Collections.enumeration;
13 import static java.util.Collections.unmodifiableSortedMap;
14 import static java.util.Collections.unmodifiableSortedSet;
15
16 import static mockit.internal.util.Utilities.JAVA8;
17
18 import edu.umd.cs.findbugs.annotations.NonNull;
19 import edu.umd.cs.findbugs.annotations.Nullable;
20
21 import java.lang.reflect.Array;
22 import java.lang.reflect.Type;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.NoSuchElementException;
26 import java.util.Optional;
27 import java.util.OptionalDouble;
28 import java.util.OptionalInt;
29 import java.util.OptionalLong;
30 import java.util.PrimitiveIterator;
31 import java.util.Spliterators;
32 import java.util.TreeMap;
33 import java.util.TreeSet;
34 import java.util.function.DoubleConsumer;
35 import java.util.function.IntConsumer;
36 import java.util.function.LongConsumer;
37 import java.util.stream.DoubleStream;
38 import java.util.stream.IntStream;
39 import java.util.stream.LongStream;
40 import java.util.stream.Stream;
41
42 import mockit.asm.types.ArrayType;
43
44
45
46
47 @SuppressWarnings("ZeroLengthArrayAllocation")
48 public final class DefaultValues {
49 private DefaultValues() {
50 }
51
52 private static final Integer ZERO_INT = 0;
53 private static final Long ZERO_LONG = 0L;
54 private static final Float ZERO_FLOAT = 0.0F;
55 private static final Double ZERO_DOUBLE = 0.0;
56 private static final Byte ZERO_BYTE = 0;
57 private static final Short ZERO_SHORT = 0;
58 private static final Character ZERO_CHAR = '\0';
59
60 private static final Map<String, Object> TYPE_DESC_TO_VALUE_MAP = new HashMap<>();
61 private static final Map<String, Object> ELEM_TYPE_TO_ONE_D_ARRAY = new HashMap<>();
62 static {
63 TYPE_DESC_TO_VALUE_MAP.put("Z", Boolean.FALSE);
64 TYPE_DESC_TO_VALUE_MAP.put("C", ZERO_CHAR);
65 TYPE_DESC_TO_VALUE_MAP.put("B", ZERO_BYTE);
66 TYPE_DESC_TO_VALUE_MAP.put("S", ZERO_SHORT);
67 TYPE_DESC_TO_VALUE_MAP.put("I", ZERO_INT);
68 TYPE_DESC_TO_VALUE_MAP.put("F", ZERO_FLOAT);
69 TYPE_DESC_TO_VALUE_MAP.put("J", ZERO_LONG);
70 TYPE_DESC_TO_VALUE_MAP.put("D", ZERO_DOUBLE);
71 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Boolean;", Boolean.FALSE);
72 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Character;", ZERO_CHAR);
73 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Byte;", ZERO_BYTE);
74 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Short;", ZERO_SHORT);
75 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Integer;", ZERO_INT);
76 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Float;", ZERO_FLOAT);
77 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Long;", ZERO_LONG);
78 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Double;", ZERO_DOUBLE);
79 TYPE_DESC_TO_VALUE_MAP.put("Ljava/lang/Iterable;", emptyList());
80 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Enumeration;", enumeration(emptyList()));
81 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Collection;", emptyList());
82 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/List;", emptyList());
83 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Set;", emptySet());
84 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/SortedSet;", unmodifiableSortedSet(new TreeSet<>()));
85 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Map;", emptyMap());
86 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/SortedMap;", unmodifiableSortedMap(new TreeMap<>()));
87 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Iterator;", emptyIterator());
88 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/ListIterator;", emptyList().listIterator());
89
90 ELEM_TYPE_TO_ONE_D_ARRAY.put("[Z", new boolean[0]);
91 ELEM_TYPE_TO_ONE_D_ARRAY.put("[C", new char[0]);
92 ELEM_TYPE_TO_ONE_D_ARRAY.put("[B", new byte[0]);
93 ELEM_TYPE_TO_ONE_D_ARRAY.put("[S", new short[0]);
94 ELEM_TYPE_TO_ONE_D_ARRAY.put("[I", new int[0]);
95 ELEM_TYPE_TO_ONE_D_ARRAY.put("[F", new float[0]);
96 ELEM_TYPE_TO_ONE_D_ARRAY.put("[J", new long[0]);
97 ELEM_TYPE_TO_ONE_D_ARRAY.put("[D", new double[0]);
98 ELEM_TYPE_TO_ONE_D_ARRAY.put("[Ljava/lang/Object;", new Object[0]);
99 ELEM_TYPE_TO_ONE_D_ARRAY.put("[Ljava/lang/String;", new String[0]);
100
101 if (JAVA8) {
102 addJava8TypeMapEntries();
103 }
104 }
105
106 private static void addJava8TypeMapEntries() {
107 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Optional;", Optional.empty());
108 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/OptionalInt;", OptionalInt.empty());
109 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/OptionalLong;", OptionalLong.empty());
110 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/OptionalDouble;", OptionalDouble.empty());
111 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Spliterator;", Spliterators.emptySpliterator());
112 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Spliterator$OfInt;", Spliterators.emptyIntSpliterator());
113 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Spliterator$OfLong;", Spliterators.emptyLongSpliterator());
114 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/Spliterator$OfDouble;", Spliterators.emptyDoubleSpliterator());
115
116 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/PrimitiveIterator$OfInt;", new PrimitiveIterator.OfInt() {
117 @Override
118 public int nextInt() {
119 throw new NoSuchElementException();
120 }
121
122 @Override
123 public Integer next() {
124 throw new NoSuchElementException();
125 }
126
127 @Override
128 public boolean hasNext() {
129 return false;
130 }
131
132 @Override
133 public void forEachRemaining(IntConsumer action) {
134 }
135 });
136 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/PrimitiveIterator$OfLong;", new PrimitiveIterator.OfLong() {
137 @Override
138 public long nextLong() {
139 throw new NoSuchElementException();
140 }
141
142 @Override
143 public Long next() {
144 throw new NoSuchElementException();
145 }
146
147 @Override
148 public boolean hasNext() {
149 return false;
150 }
151
152 @Override
153 public void forEachRemaining(LongConsumer action) {
154 }
155 });
156 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/PrimitiveIterator$OfDouble;", new PrimitiveIterator.OfDouble() {
157 @Override
158 public double nextDouble() {
159 throw new NoSuchElementException();
160 }
161
162 @Override
163 public Double next() {
164 throw new NoSuchElementException();
165 }
166
167 @Override
168 public boolean hasNext() {
169 return false;
170 }
171
172 @Override
173 public void forEachRemaining(DoubleConsumer action) {
174 }
175 });
176
177
178
179 try {
180 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/stream/Stream;", Stream.class.getMethod("empty").invoke(null));
181 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/stream/IntStream;", IntStream.class.getMethod("empty").invoke(null));
182 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/stream/LongStream;",
183 LongStream.class.getMethod("empty").invoke(null));
184 TYPE_DESC_TO_VALUE_MAP.put("Ljava/util/stream/DoubleStream;",
185 DoubleStream.class.getMethod("empty").invoke(null));
186 } catch (Exception ignore) {
187 }
188 }
189
190 @NonNull
191 public static String getReturnTypeDesc(@NonNull String methodNameAndDesc) {
192 int rightParen = methodNameAndDesc.indexOf(')') + 1;
193 return methodNameAndDesc.substring(rightParen);
194 }
195
196 @Nullable
197 public static Object computeForType(@NonNull String typeDesc) {
198 char typeDescChar = typeDesc.charAt(0);
199
200 if (typeDescChar == 'V') {
201 return null;
202 }
203
204 Object defaultValue = TYPE_DESC_TO_VALUE_MAP.get(typeDesc);
205
206 if (defaultValue != null) {
207 return defaultValue;
208 }
209
210 if (typeDescChar == 'L') {
211 return null;
212 }
213
214
215 return computeForArrayType(typeDesc);
216 }
217
218 @NonNull
219 public static Object computeForArrayType(@NonNull String typeDesc) {
220 Object emptyArray = ELEM_TYPE_TO_ONE_D_ARRAY.get(typeDesc);
221
222 if (emptyArray == null) {
223 emptyArray = newEmptyArray(typeDesc);
224 }
225
226 return emptyArray;
227 }
228
229 @NonNull
230 private static Object newEmptyArray(@NonNull String typeDesc) {
231 ArrayType type = ArrayType.create(typeDesc);
232 Class<?> elementType = TypeDescriptor.getClassForType(type.getElementType());
233
234 return Array.newInstance(elementType, new int[type.getDimensions()]);
235 }
236
237 @Nullable
238 public static Object computeForType(@NonNull Class<?> type) {
239 if (type.isArray()) {
240 return Array.newInstance(type.getComponentType(), 0);
241 }
242
243 if (type != void.class && type.isPrimitive()) {
244 return defaultValueForPrimitiveType(type);
245 }
246
247 return computeForWrapperType(type);
248 }
249
250 @NonNull
251 public static Object defaultValueForPrimitiveType(@NonNull Class<?> type) {
252 if (type == int.class) {
253 return ZERO_INT;
254 }
255 if (type == boolean.class) {
256 return Boolean.FALSE;
257 }
258 if (type == long.class) {
259 return ZERO_LONG;
260 }
261 if (type == double.class) {
262 return ZERO_DOUBLE;
263 }
264 if (type == float.class) {
265 return ZERO_FLOAT;
266 }
267 if (type == char.class) {
268 return ZERO_CHAR;
269 }
270 if (type == byte.class) {
271 return ZERO_BYTE;
272 }
273 return ZERO_SHORT;
274 }
275
276 @Nullable
277 @SuppressWarnings("unchecked")
278 public static <T> T computeForWrapperType(@NonNull Type type) {
279 if (type == Integer.class) {
280 return (T) ZERO_INT;
281 }
282
283 if (type == Boolean.class) {
284 return (T) Boolean.FALSE;
285 }
286
287 if (type == Long.class) {
288 return (T) ZERO_LONG;
289 }
290
291 if (type == Double.class) {
292 return (T) ZERO_DOUBLE;
293 }
294
295 if (type == Float.class) {
296 return (T) ZERO_FLOAT;
297 }
298
299 if (type == Character.class) {
300 return (T) ZERO_CHAR;
301 }
302
303 if (type == Byte.class) {
304 return (T) ZERO_BYTE;
305 }
306
307 if (type == Short.class) {
308 return (T) ZERO_SHORT;
309 }
310
311 return null;
312 }
313
314 @Nullable
315 public static Object computeForReturnType(@NonNull String methodNameAndDesc) {
316 String typeDesc = getReturnTypeDesc(methodNameAndDesc);
317 return computeForType(typeDesc);
318 }
319 }