Skip to content
This repository has been archived by the owner on Jan 10, 2022. It is now read-only.

Commit

Permalink
Merge pull request #59 from diffblue/antonia/objenesis-everywhere
Browse files Browse the repository at this point in the history
Always use Objenesis to instantiate objects
  • Loading branch information
antlechner committed Dec 19, 2018
2 parents f69ff3f + 0987f74 commit 96d1abe
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 94 deletions.
101 changes: 7 additions & 94 deletions src/main/java/com/diffblue/deeptestutils/Reflector.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.diffblue.deeptestutils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
Expand All @@ -13,7 +12,6 @@
import javassist.CtField;
import javassist.CtMember;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.NotFoundException;

Expand Down Expand Up @@ -334,40 +332,14 @@ private static void makePublic(final CtClass c) {
}

/**
* This forces the creation of an instance for a given class. If the class
* provides a public default constructor, it is called. If the class has a
* private default constructor, it is made accessible and is then called. Else
* we use Objenesis to force creation of an instance.
*
* There are several exceptions that `Constructor.newInstance` can throw. We
* catch `IllegalAccessException` and `InstantiationException` and continue
* with Objenesis in those cases. `IllegalArgumentException` cannot be thrown
* as we only try to use the default constructor which has no parameters. An
* `InvocationTargetException` signals an exception in the constructor and is
* passed to the calling method.
* This forces the creation of an instance for a given class using Objenesis.
*
* @param <T> type parameter of the class
* @param cl a <code>Class</code> the class to instantiate
* @return an <code>Object</code> which is an instance of the specified class
*
* @throws InvocationTargetException if the default constructor or a static
* initializer throws an exception.
*/
@SuppressWarnings("unchecked")
public static <T> T getInstance(final Class<T> cl)
throws InvocationTargetException {
Constructor<?> ctor = getDefaultConstructor(cl);
if (ctor != null) {
Constructor<?> defaultCtor = ctor;
defaultCtor.setAccessible(true);
try {
return (T) defaultCtor.newInstance();
} catch (InstantiationException ex) {
return (T) new ObjenesisStd().newInstance(cl);
} catch (IllegalAccessException ex) {
return (T) new ObjenesisStd().newInstance(cl);
}
}
public static <T> T getInstance(final Class<T> cl) {
return (T) new ObjenesisStd().newInstance(cl);
}

Expand All @@ -378,12 +350,11 @@ public static <T> T getInstance(final Class<T> cl)
* @param className a <code>String</code> giving the name of the class
* @return an <code>Object</code> which is an instance of the specified class
*
* @throws InvocationTargetException if the constructor of a class
* throws an exception
*
* In the case of an `ExceptionInInitializerError` from `newInstance`, which
* signals an exception in a static initializer, we extract its cause and
* throw an `InvocationTargetException`.
* @throws InvocationTargetException if an `ExceptionInInitializerError` was
* thrown by Class.forName, which signals an exception in the static
* initializer of the class. In this case we extract the cause of the error
* and wrap it within an `InvocationTargetException`.
* TODO This currently only works for concrete classes, see TG-5895.
*/
public static <T> Object getInstance(final String className)
throws InvocationTargetException {
Expand Down Expand Up @@ -428,47 +399,6 @@ public static <T> Object getInstance(final String className)
}
}

// In the case of an abstract class, search any constructor in
// the superclass and add an empty one with the same types.
// The class must have a constructor.
if (!cl.isInterface()) {
try {
CtConstructor[] ctors = cl.getConstructors();
if (ctors.length > 0) {
CtConstructor newCtor =
CtNewConstructor.make(
ctors[0].getParameterTypes(),
ctors[0].getExceptionTypes(),
implementation);
implementation.addConstructor(newCtor);
} else {
throw new DeeptestUtilsRuntimeException(
"Cannot find constructor in abstract class",
new NotFoundException(
"Cannot find constructor in abstract class"));
}
} catch (CannotCompileException e) {
throw new
DeeptestUtilsRuntimeException(e.getMessage(), e.getCause());
} catch (NotFoundException e) {
throw new
DeeptestUtilsRuntimeException(e.getMessage(), e.getCause());
}

} else {
// in the case of an interface, simply add a default
// constructor
try {
CtConstructor newCtor =
new CtConstructor(new CtClass[] {}, implementation);
newCtor.setBody("{}");
implementation.addConstructor(newCtor);
} catch (CannotCompileException e) {
throw new
DeeptestUtilsRuntimeException(e.getMessage(), e.getCause());
}
}

// declared methods or only methods ?
try {
for (CtMethod m : cl.getDeclaredMethods()) {
Expand Down Expand Up @@ -551,21 +481,4 @@ private static boolean isAbstract(final CtClass c) {
}
return false;
}

/**
* Returns the default constructor if one exists.
*
* @param c the class to search the constructor in
* @return a value holding a <code>Constructor</code>
* object if it exists, else null
*/
private static Constructor<?> getDefaultConstructor(
final Class<?> c) {
for (Constructor ctor : c.getDeclaredConstructors()) {
if (ctor.getParameterTypes().length == 0) {
return ctor;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.diffblue.deeptestutils.regression;

public class ClassWithConstructor {

public static Integer integerField;

public int intField;

public ClassWithConstructor() {
intField = integerField.intValue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.diffblue.deeptestutils.regression;

import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import com.diffblue.deeptestutils.Reflector;

public class ReflectorGetInstanceTest {

// This test checks that in a class with a constructor with no arguments,
// calling Reflector.getInstance does not cause a call to that constructor.
// If the constructor was called, a NullPointerException would happen as
// integerField has its default value of null.
@Test
public void checkNoConstructorCall() throws InvocationTargetException {
ClassWithConstructor cwc = (ClassWithConstructor) Reflector.getInstance
("com.diffblue.deeptestutils.regression.ClassWithConstructor");
Assert.assertEquals(cwc.intField, 0);
}
}

0 comments on commit 96d1abe

Please sign in to comment.