36
36
import java .util .LinkedHashSet ;
37
37
import java .util .List ;
38
38
import java .util .Map ;
39
- import java .util .Queue ;
40
- import java .util .Set ;
41
- import java .util .SortedMap ;
42
- import java .util .SortedSet ;
43
39
import java .util .TreeMap ;
44
40
import java .util .TreeSet ;
45
41
import java .util .concurrent .ConcurrentHashMap ;
46
- import java .util .concurrent .ConcurrentMap ;
47
- import java .util .concurrent .ConcurrentNavigableMap ;
48
42
import java .util .concurrent .ConcurrentSkipListMap ;
49
43
50
44
/** Returns a function that can construct an instance of a requested type. */
@@ -94,7 +88,19 @@ static String checkInstantiable(Class<?> c) {
94
88
return null ;
95
89
}
96
90
91
+ /** Calls {@link #get(TypeToken, boolean)}, and allows usage of JDK Unsafe. */
97
92
public <T > ObjectConstructor <T > get (TypeToken <T > typeToken ) {
93
+ return get (typeToken , true );
94
+ }
95
+
96
+ /**
97
+ * Retrieves an object constructor for the given type.
98
+ *
99
+ * @param typeToken type for which a constructor should be retrieved
100
+ * @param allowUnsafe whether to allow usage of JDK Unsafe; has no effect if {@link #useJdkUnsafe}
101
+ * is false
102
+ */
103
+ public <T > ObjectConstructor <T > get (TypeToken <T > typeToken , boolean allowUnsafe ) {
98
104
Type type = typeToken .getType ();
99
105
Class <? super T > rawType = typeToken .getRawType ();
100
106
@@ -142,12 +148,19 @@ public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
142
148
};
143
149
}
144
150
151
+ if (!allowUnsafe ) {
152
+ String message =
153
+ "Unable to create instance of "
154
+ + rawType
155
+ + "; Register an InstanceCreator or a TypeAdapter for this type." ;
156
+ return () -> {
157
+ throw new JsonIOException (message );
158
+ };
159
+ }
160
+
145
161
// Consider usage of Unsafe as reflection, so don't use if BLOCK_ALL
146
162
// Additionally, since it is not calling any constructor at all, don't use if BLOCK_INACCESSIBLE
147
- if (filterResult == FilterResult .ALLOW ) {
148
- // finally try unsafe
149
- return newUnsafeAllocator (rawType );
150
- } else {
163
+ if (filterResult != FilterResult .ALLOW ) {
151
164
String message =
152
165
"Unable to create instance of "
153
166
+ rawType
@@ -158,6 +171,9 @@ public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
158
171
throw new JsonIOException (message );
159
172
};
160
173
}
174
+
175
+ // finally try unsafe
176
+ return newUnsafeAllocator (rawType );
161
177
}
162
178
163
179
/**
@@ -290,7 +306,6 @@ private static <T> ObjectConstructor<T> newDefaultConstructor(
290
306
}
291
307
292
308
/** Constructors for common interface types like Map and List and their subtypes. */
293
- @ SuppressWarnings ("unchecked" ) // use runtime checks to guarantee that 'T' is what it is
294
309
private static <T > ObjectConstructor <T > newDefaultImplementationConstructor (
295
310
Type type , Class <? super T > rawType ) {
296
311
@@ -303,33 +318,84 @@ private static <T> ObjectConstructor<T> newDefaultImplementationConstructor(
303
318
*/
304
319
305
320
if (Collection .class .isAssignableFrom (rawType )) {
306
- if (SortedSet .class .isAssignableFrom (rawType )) {
307
- return () -> (T ) new TreeSet <>();
308
- } else if (Set .class .isAssignableFrom (rawType )) {
309
- return () -> (T ) new LinkedHashSet <>();
310
- } else if (Queue .class .isAssignableFrom (rawType )) {
311
- return () -> (T ) new ArrayDeque <>();
312
- } else {
313
- return () -> (T ) new ArrayList <>();
314
- }
321
+ @ SuppressWarnings ("unchecked" )
322
+ ObjectConstructor <T > constructor = (ObjectConstructor <T >) newCollectionConstructor (rawType );
323
+ return constructor ;
315
324
}
316
325
317
326
if (Map .class .isAssignableFrom (rawType )) {
318
- if (ConcurrentNavigableMap .class .isAssignableFrom (rawType )) {
319
- return () -> (T ) new ConcurrentSkipListMap <>();
320
- } else if (ConcurrentMap .class .isAssignableFrom (rawType )) {
321
- return () -> (T ) new ConcurrentHashMap <>();
322
- } else if (SortedMap .class .isAssignableFrom (rawType )) {
323
- return () -> (T ) new TreeMap <>();
324
- } else if (type instanceof ParameterizedType
325
- && !String .class .isAssignableFrom (
326
- TypeToken .get (((ParameterizedType ) type ).getActualTypeArguments ()[0 ]).getRawType ())) {
327
- return () -> (T ) new LinkedHashMap <>();
328
- } else {
329
- return () -> (T ) new LinkedTreeMap <>();
330
- }
327
+ @ SuppressWarnings ("unchecked" )
328
+ ObjectConstructor <T > constructor = (ObjectConstructor <T >) newMapConstructor (type , rawType );
329
+ return constructor ;
330
+ }
331
+
332
+ // Unsupported type; try other means of creating constructor
333
+ return null ;
334
+ }
335
+
336
+ private static ObjectConstructor <? extends Collection <? extends Object >> newCollectionConstructor (
337
+ Class <?> rawType ) {
338
+
339
+ // First try List implementation
340
+ if (rawType .isAssignableFrom (ArrayList .class )) {
341
+ return () -> new ArrayList <>();
342
+ }
343
+ // Then try Set implementation
344
+ else if (rawType .isAssignableFrom (LinkedHashSet .class )) {
345
+ return () -> new LinkedHashSet <>();
346
+ }
347
+ // Then try SortedSet / NavigableSet implementation
348
+ else if (rawType .isAssignableFrom (TreeSet .class )) {
349
+ return () -> new TreeSet <>();
350
+ }
351
+ // Then try Queue implementation
352
+ else if (rawType .isAssignableFrom (ArrayDeque .class )) {
353
+ return () -> new ArrayDeque <>();
354
+ }
355
+
356
+ // Was unable to create matching Collection constructor
357
+ return null ;
358
+ }
359
+
360
+ private static boolean hasStringKeyType (Type mapType ) {
361
+ // If mapType is not parameterized, assume it might have String as key type
362
+ if (!(mapType instanceof ParameterizedType )) {
363
+ return true ;
364
+ }
365
+
366
+ Type [] typeArguments = ((ParameterizedType ) mapType ).getActualTypeArguments ();
367
+ if (typeArguments .length == 0 ) {
368
+ return false ;
369
+ }
370
+ return $Gson$Types .getRawType (typeArguments [0 ]) == String .class ;
371
+ }
372
+
373
+ private static ObjectConstructor <? extends Map <? extends Object , Object >> newMapConstructor (
374
+ Type type , Class <?> rawType ) {
375
+ // First try Map implementation
376
+ /*
377
+ * Legacy special casing for Map<String, ...> to avoid DoS from colliding String hashCode
378
+ * values for older JDKs; use own LinkedTreeMap<String, Object> instead
379
+ */
380
+ if (rawType .isAssignableFrom (LinkedTreeMap .class ) && hasStringKeyType (type )) {
381
+ return () -> new LinkedTreeMap <>();
382
+ } else if (rawType .isAssignableFrom (LinkedHashMap .class )) {
383
+ return () -> new LinkedHashMap <>();
384
+ }
385
+ // Then try SortedMap / NavigableMap implementation
386
+ else if (rawType .isAssignableFrom (TreeMap .class )) {
387
+ return () -> new TreeMap <>();
388
+ }
389
+ // Then try ConcurrentMap implementation
390
+ else if (rawType .isAssignableFrom (ConcurrentHashMap .class )) {
391
+ return () -> new ConcurrentHashMap <>();
392
+ }
393
+ // Then try ConcurrentNavigableMap implementation
394
+ else if (rawType .isAssignableFrom (ConcurrentSkipListMap .class )) {
395
+ return () -> new ConcurrentSkipListMap <>();
331
396
}
332
397
398
+ // Was unable to create matching Map constructor
333
399
return null ;
334
400
}
335
401
0 commit comments