View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
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   * Provides default values for each type, typically used for returning default values according to method return types.
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         // These are static interface methods, which can't be compiled on "-source 1.7".
178         // noinspection OverlyBroadCatchBlock
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         // It's an array.
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 }