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