]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2011 Google Inc. | |
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 | ||
17 | package com.google.gson.internal; | |
18 | ||
19 | import java.io.ObjectInputStream; | |
20 | import java.io.ObjectStreamClass; | |
21 | import java.lang.reflect.Field; | |
22 | import java.lang.reflect.Method; | |
23 | ||
24 | /** | |
25 | * Do sneaky things to allocate objects without invoking their constructors. | |
26 | * | |
27 | * @author Joel Leitch | |
28 | * @author Jesse Wilson | |
29 | */ | |
30 | public abstract class UnsafeAllocator { | |
31 | public abstract <T> T newInstance(Class<T> c) throws Exception; | |
32 | ||
33 | public static UnsafeAllocator create() { | |
34 | // try JVM | |
35 | // public class Unsafe { | |
36 | // public Object allocateInstance(Class<?> type); | |
37 | // } | |
38 | try { | |
39 | Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); | |
40 | Field f = unsafeClass.getDeclaredField("theUnsafe"); | |
41 | f.setAccessible(true); | |
42 | final Object unsafe = f.get(null); | |
43 | final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class); | |
44 | return new UnsafeAllocator() { | |
45 | @Override | |
46 | @SuppressWarnings("unchecked") | |
47 | public <T> T newInstance(Class<T> c) throws Exception { | |
48 | return (T) allocateInstance.invoke(unsafe, c); | |
49 | } | |
50 | }; | |
51 | } catch (Exception ignored) { | |
52 | } | |
53 | ||
54 | // try dalvikvm, pre-gingerbread | |
55 | // public class ObjectInputStream { | |
56 | // private static native Object newInstance( | |
57 | // Class<?> instantiationClass, Class<?> constructorClass); | |
58 | // } | |
59 | try { | |
60 | final Method newInstance = ObjectInputStream.class | |
61 | .getDeclaredMethod("newInstance", Class.class, Class.class); | |
62 | newInstance.setAccessible(true); | |
63 | return new UnsafeAllocator() { | |
64 | @Override | |
65 | @SuppressWarnings("unchecked") | |
66 | public <T> T newInstance(Class<T> c) throws Exception { | |
67 | return (T) newInstance.invoke(null, c, Object.class); | |
68 | } | |
69 | }; | |
70 | } catch (Exception ignored) { | |
71 | } | |
72 | ||
73 | // try dalvikvm, post-gingerbread | |
74 | // public class ObjectStreamClass { | |
75 | // private static native int getConstructorId(Class<?> c); | |
76 | // private static native Object newInstance(Class<?> instantiationClass, int methodId); | |
77 | // } | |
78 | try { | |
79 | Method getConstructorId = ObjectStreamClass.class | |
80 | .getDeclaredMethod("getConstructorId", Class.class); | |
81 | getConstructorId.setAccessible(true); | |
82 | final int constructorId = (Integer) getConstructorId.invoke(null, Object.class); | |
83 | final Method newInstance = ObjectStreamClass.class | |
84 | .getDeclaredMethod("newInstance", Class.class, int.class); | |
85 | newInstance.setAccessible(true); | |
86 | return new UnsafeAllocator() { | |
87 | @Override | |
88 | @SuppressWarnings("unchecked") | |
89 | public <T> T newInstance(Class<T> c) throws Exception { | |
90 | return (T) newInstance.invoke(null, c, constructorId); | |
91 | } | |
92 | }; | |
93 | } catch (Exception ignored) { | |
94 | } | |
95 | ||
96 | // give up | |
97 | return new UnsafeAllocator() { | |
98 | @Override | |
99 | public <T> T newInstance(Class<T> c) { | |
100 | throw new UnsupportedOperationException("Cannot allocate " + c); | |
101 | } | |
102 | }; | |
103 | } | |
104 | } |