Add FinalTest to ensure the compiler in-lining behavior does not change in the future

This commit is contained in:
Travis Burtrum 2015-02-10 15:47:02 -05:00
parent c82f7ed3fc
commit bb40891e14
2 changed files with 253 additions and 0 deletions

View File

@ -0,0 +1,64 @@
package com.moparisthebest.jdbc;
import com.moparisthebest.jdbc.dto.FinalDTO;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
/**
* This test ensures the way the java compiler in-lines final variables today
* is the same way it will in-line them tomorrow, since changes in this behavior
* might silently break classes that are filled with reflection.
*/
public class FinalTest {
private FinalDTO object;
@Before
public void before(){
object = new FinalDTO();
assertTrue(object.directEqualsReflection());
}
@Test
public void testFinalPrimitiveLongDirectCantBeSet() {
assertFalse(object.setField("finalPrimitiveLongDirect", 1L));
}
@Test
public void testFinalStringDirectCantBeSet() {
assertFalse(object.setField("finalStringDirect", "1"));
}
@Test
public void testEffectiveFinalPrimitiveLongDirectCanBeSet() {
assertTrue(object.setField("effectiveFinalPrimitiveLongDirect", 1L));
}
@Test
public void testEffectiveFinalStringDirectCanBeSet() {
assertTrue(object.setField("effectiveFinalStringDirect", "1"));
}
@Test
public void testFinalPrimitiveLongConstructorCanBeSet() {
assertTrue(object.setField("finalPrimitiveLongConstructor", 1L));
}
@Test
public void testFinalStringConstructorCanBeSet() {
assertTrue(object.setField("finalStringConstructor", "1"));
}
@Test
public void testFinalLongDirectCanBeSet() {
assertTrue(object.setField("finalLongDirect", 1L));
}
@Test
public void testFinalLongConstructorCanBeSet() {
assertTrue(object.setField("finalLongConstructor", 1L));
}
}

View File

@ -0,0 +1,189 @@
package com.moparisthebest.jdbc.dto;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.Objects;
/**
* This class is meant to test the java compiler's in-lining behavior for final variables
*/
public class FinalDTO {
private static final boolean useReflectionNotUnsafe = true;
private final long finalPrimitiveLongDirect = 2L;
private long effectiveFinalPrimitiveLongDirect = 2L;
private final long finalPrimitiveLongConstructor;
{
this.finalPrimitiveLongConstructor = 2L;
}
private final String finalStringDirect = "2";
private String effectiveFinalStringDirect = "2";
private final String finalStringConstructor;
{
this.finalStringConstructor = "2";
}
private final Long finalLongDirect = 2L;
private final Long finalLongConstructor;
{
this.finalLongConstructor = 2L;
}
public long getFinalPrimitiveLongDirect() {
return finalPrimitiveLongDirect;
}
public long getEffectiveFinalPrimitiveLongDirect() {
return effectiveFinalPrimitiveLongDirect;
}
public long getFinalPrimitiveLongConstructor() {
return finalPrimitiveLongConstructor;
}
public String getFinalStringDirect() {
return finalStringDirect;
}
public String getEffectiveFinalStringDirect() {
return effectiveFinalStringDirect;
}
public String getFinalStringConstructor() {
return finalStringConstructor;
}
public Long getFinalLongDirect() {
return finalLongDirect;
}
public Long getFinalLongConstructor() {
return finalLongConstructor;
}
@Override
public String toString() {
return "FinalTest{" +
"finalPrimitiveLongDirect=" + finalPrimitiveLongDirect +
", reflection finalPrimitiveLongDirect=" + getField("finalPrimitiveLongDirect") +
", effectiveFinalPrimitiveLongDirect=" + effectiveFinalPrimitiveLongDirect +
", reflection effectiveFinalPrimitiveLongDirect=" + getField("effectiveFinalPrimitiveLongDirect") +
", finalPrimitiveLongConstructor=" + finalPrimitiveLongConstructor +
", reflection finalPrimitiveLongConstructor=" + getField("finalPrimitiveLongConstructor") +
", finalStringDirect=" + finalStringDirect +
", reflection finalStringDirect=" + getField("finalStringDirect") +
", effectiveFinalStringDirect=" + effectiveFinalStringDirect +
", reflection effectiveFinalStringDirect=" + getField("effectiveFinalStringDirect") +
", finalStringConstructor=" + finalStringConstructor +
", reflection finalStringConstructor=" + getField("finalStringConstructor") +
", finalLongDirect=" + finalLongDirect +
", reflection finalLongDirect=" + getField("finalLongDirect") +
", finalLongConstructor=" + finalLongConstructor +
", reflection finalLongConstructor=" + getField("finalLongConstructor") +
'}';
}
public boolean directEqualsReflection() {
return directEqualsReflection(finalPrimitiveLongDirect, "finalPrimitiveLongDirect")
&& directEqualsReflection(effectiveFinalPrimitiveLongDirect, "effectiveFinalPrimitiveLongDirect")
&& directEqualsReflection(finalPrimitiveLongConstructor, "finalPrimitiveLongConstructor")
&& directEqualsReflection(finalStringDirect, "finalStringDirect")
&& directEqualsReflection(effectiveFinalStringDirect, "effectiveFinalStringDirect")
&& directEqualsReflection(finalStringConstructor, "finalStringConstructor")
&& directEqualsReflection(finalLongDirect, "finalLongDirect")
&& directEqualsReflection(finalLongConstructor, "finalLongConstructor")
;
}
private boolean directEqualsReflection(final Object object, final String fieldName) {
return Objects.equals(object, getField(fieldName));
}
public Object getField(final String fieldName) {
return getField(this, fieldName);
}
public boolean setField(final String fieldName, final Object fieldValue) {
setField(this, fieldName, fieldValue);
return directEqualsReflection();
}
private static Object getField(final Object object, final String fieldName) {
try {
final Field field = object.getClass().getDeclaredField(fieldName);
if (useReflectionNotUnsafe) {
field.setAccessible(true);
return field.get(object);
} else {
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
final Unsafe unsafe = (Unsafe) unsafeField.get(null);
final long offset = unsafe.objectFieldOffset(field);
if (field.getType().isPrimitive()) {
if (field.getType().equals(long.class))
return unsafe.getLong(object, offset);
else
throw new RuntimeException("unsupported primitive type");
} else
return unsafe.getObject(object, offset);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
private static void setField(final Object object, final String fieldName, final Object fieldValue) {
try {
final Field field = object.getClass().getDeclaredField(fieldName);
if (useReflectionNotUnsafe) {
field.setAccessible(true);
field.set(object, fieldValue);
} else {
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
final Unsafe unsafe = (Unsafe) unsafeField.get(null);
final long offset = unsafe.objectFieldOffset(field);
if (field.getType().isPrimitive()) {
if (fieldValue instanceof Long)
unsafe.putLong(object, offset, (Long) fieldValue);
else
throw new RuntimeException("unsupported primitive type");
} else
unsafe.putObject(object, offset, fieldValue);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static void main(final String[] args) {
final FinalDTO object = new FinalDTO();
System.out.println("1: " + object);
setField(object, "finalPrimitiveLongDirect", 1L);
System.out.println("2: " + object);
setField(object, "finalPrimitiveLongConstructor", 1L);
System.out.println("3: " + object);
setField(object, "finalStringDirect", "1");
System.out.println("4: " + object);
setField(object, "finalStringConstructor", "1");
System.out.println("5: " + object);
setField(object, "finalLongDirect", 1L);
System.out.println("6: " + object);
setField(object, "finalLongConstructor", 1L);
System.out.println("7: " + object);
setField(object, "effectiveFinalPrimitiveLongDirect", 1L);
System.out.println("8: " + object);
setField(object, "effectiveFinalStringDirect", "1");
System.out.println("9: " + object);
}
}