diff --git a/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java index fce0be3791..41e7cd1560 100644 --- a/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java +++ b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java @@ -20,6 +20,7 @@ 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. @@ -45,6 +46,7 @@ public static UnsafeAllocator create() { @Override @SuppressWarnings("unchecked") public T newInstance(Class c) throws Exception { + assertInstantiable(c); return (T) allocateInstance.invoke(unsafe, c); } }; @@ -68,6 +70,7 @@ public T newInstance(Class c) throws Exception { @Override @SuppressWarnings("unchecked") public T newInstance(Class c) throws Exception { + assertInstantiable(c); return (T) newInstance.invoke(null, c, constructorId); } }; @@ -87,6 +90,7 @@ public T newInstance(Class c) throws Exception { @Override @SuppressWarnings("unchecked") public T newInstance(Class c) throws Exception { + assertInstantiable(c); return (T) newInstance.invoke(null, c, Object.class); } }; @@ -101,4 +105,19 @@ public T newInstance(Class c) { } }; } + + /** + * Check if the class can be instantiated by unsafe allocator. If the instance has interface or abstract modifiers + * throw an {@link java.lang.UnsupportedOperationException} + * @param c instance of the class to be checked + */ + private static void assertInstantiable(Class c) { + int modifiers = c.getModifiers(); + if (Modifier.isInterface(modifiers)) { + throw new UnsupportedOperationException("Interface can't be instantiated! Interface name: " + c.getName()); + } + if (Modifier.isAbstract(modifiers)) { + throw new UnsupportedOperationException("Abstract class can't be instantiated! Class name: " + c.getName()); + } + } } diff --git a/gson/src/test/java/com/google/gson/internal/UnsafeAllocatorInstantiationTest.java b/gson/src/test/java/com/google/gson/internal/UnsafeAllocatorInstantiationTest.java new file mode 100644 index 0000000000..431fc83ddc --- /dev/null +++ b/gson/src/test/java/com/google/gson/internal/UnsafeAllocatorInstantiationTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gson.internal; + +import junit.framework.TestCase; + +/** + * Test unsafe allocator instantiation + * @author Ugljesa Jovanovic + */ +public final class UnsafeAllocatorInstantiationTest extends TestCase { + + public interface Interface { + } + + public static abstract class AbstractClass { + } + + public static class ConcreteClass { + } + + /** + * Ensure that the {@link java.lang.UnsupportedOperationException} is thrown when trying + * to instantiate an interface + */ + public void testInterfaceInstantiation() { + UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); + try { + unsafeAllocator.newInstance(Interface.class); + fail(); + } catch (Exception e) { + assertEquals(e.getClass(), UnsupportedOperationException.class); + } + } + + /** + * Ensure that the {@link java.lang.UnsupportedOperationException} is thrown when trying + * to instantiate an abstract class + */ + public void testAbstractClassInstantiation() { + UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); + try { + unsafeAllocator.newInstance(AbstractClass.class); + fail(); + } catch (Exception e) { + assertEquals(e.getClass(), UnsupportedOperationException.class); + } + } + + /** + * Ensure that no exception is thrown when trying to instantiate a concrete class + */ + public void testConcreteClassInstantiation() { + UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); + try { + unsafeAllocator.newInstance(ConcreteClass.class); + } catch (Exception e) { + fail(); + } + } +}