Add FinalTest to ensure the compiler in-lining behavior does not change in the future
This commit is contained in:
parent
c82f7ed3fc
commit
bb40891e14
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user