Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only create one UnsafeAllocator instance #2196

Merged
merged 2 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators, bo
this.reflectionFilters = reflectionFilters;
}

/**
* Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
* return an exception message.
* @param c instance of the class to be checked
* @return if instantiable {@code null}, else a non-{@code null} exception message
*/
static String checkInstantiable(Class<?> c) {
int modifiers = c.getModifiers();
if (Modifier.isInterface(modifiers)) {
return "Interfaces can't be instantiated! Register an InstanceCreator "
+ "or a TypeAdapter for this type. Interface name: " + c.getName();
}
if (Modifier.isAbstract(modifiers)) {
return "Abstract classes can't be instantiated! Register an InstanceCreator "
+ "or a TypeAdapter for this type. Class name: " + c.getName();
}
return null;
}

public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
final Type type = typeToken.getType();
final Class<? super T> rawType = typeToken.getRawType();
Expand Down Expand Up @@ -110,7 +129,7 @@ public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {

// Check whether type is instantiable; otherwise ReflectionAccessFilter recommendation
// of adjusting filter suggested below is irrelevant since it would not solve the problem
final String exceptionMessage = UnsafeAllocator.checkInstantiable(rawType);
final String exceptionMessage = checkInstantiable(rawType);
if (exceptionMessage != null) {
return new ObjectConstructor<T>() {
@Override public T construct() {
Expand Down Expand Up @@ -342,11 +361,10 @@ private static <T> ObjectConstructor<T> newDefaultImplementationConstructor(
private <T> ObjectConstructor<T> newUnsafeAllocator(final Class<? super T> rawType) {
if (useJdkUnsafe) {
return new ObjectConstructor<T>() {
private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
@Override public T construct() {
try {
@SuppressWarnings("unchecked")
T newInstance = (T) unsafeAllocator.newInstance(rawType);
T newInstance = (T) UnsafeAllocator.INSTANCE.newInstance(rawType);
return newInstance;
} catch (Exception e) {
throw new RuntimeException(("Unable to create instance of " + rawType + ". "
Expand Down
26 changes: 4 additions & 22 deletions gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.io.ObjectStreamClass;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
* Do sneaky things to allocate objects without invoking their constructors.
Expand All @@ -31,38 +30,21 @@
public abstract class UnsafeAllocator {
public abstract <T> T newInstance(Class<T> c) throws Exception;

/**
* Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
* return an exception message.
* @param c instance of the class to be checked
* @return if instantiable {@code null}, else a non-{@code null} exception message
*/
static String checkInstantiable(Class<?> c) {
int modifiers = c.getModifiers();
if (Modifier.isInterface(modifiers)) {
return "Interfaces can't be instantiated! Register an InstanceCreator "
+ "or a TypeAdapter for this type. Interface name: " + c.getName();
}
if (Modifier.isAbstract(modifiers)) {
return "Abstract classes can't be instantiated! Register an InstanceCreator "
+ "or a TypeAdapter for this type. Class name: " + c.getName();
}
return null;
}

/**
* Asserts that the class is instantiable. This check should have already occurred
* in {@link ConstructorConstructor}; this check here acts as safeguard since trying
* to use Unsafe for non-instantiable classes might crash the JVM on some devices.
*/
private static void assertInstantiable(Class<?> c) {
String exceptionMessage = checkInstantiable(c);
String exceptionMessage = ConstructorConstructor.checkInstantiable(c);
if (exceptionMessage != null) {
throw new AssertionError("UnsafeAllocator is used for non-instantiable type: " + exceptionMessage);
}
}

public static UnsafeAllocator create() {
public static final UnsafeAllocator INSTANCE = create();

private static UnsafeAllocator create() {
// try JVM
// public class Unsafe {
// public Object allocateInstance(Class<?> type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ public static class ConcreteClass {
* to instantiate an interface
*/
public void testInterfaceInstantiation() throws Exception {
UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
try {
unsafeAllocator.newInstance(Interface.class);
UnsafeAllocator.INSTANCE.newInstance(Interface.class);
fail();
} catch (AssertionError e) {
assertTrue(e.getMessage().startsWith("UnsafeAllocator is used for non-instantiable type"));
Expand All @@ -51,9 +50,8 @@ public void testInterfaceInstantiation() throws Exception {
* to instantiate an abstract class
*/
public void testAbstractClassInstantiation() throws Exception {
UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
try {
unsafeAllocator.newInstance(AbstractClass.class);
UnsafeAllocator.INSTANCE.newInstance(AbstractClass.class);
fail();
} catch (AssertionError e) {
assertTrue(e.getMessage().startsWith("UnsafeAllocator is used for non-instantiable type"));
Expand All @@ -64,8 +62,7 @@ public void testAbstractClassInstantiation() throws Exception {
* Ensure that no exception is thrown when trying to instantiate a concrete class
*/
public void testConcreteClassInstantiation() throws Exception {
UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
ConcreteClass instance = unsafeAllocator.newInstance(ConcreteClass.class);
ConcreteClass instance = UnsafeAllocator.INSTANCE.newInstance(ConcreteClass.class);
assertNotNull(instance);
}
}