Skip to content

Commit

Permalink
add Whitebox.setInternalFinalState()
Browse files Browse the repository at this point in the history
Change-Id: I2e2fd25d95dc3033ac1fa332fe6ed56b3c259758
  • Loading branch information
javeme committed Apr 24, 2022
1 parent f2e8384 commit 6b0d97e
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Objects;

Expand All @@ -32,13 +33,18 @@ public class Whitebox {

public static final char SEPARATOR = '.';

public static void setInternalState(Object target, String fieldName,
Object value) {
public static void setInternalState(Object target,
String fieldName, Object value) {
assert target != null;
assert fieldName != null;
int sep = fieldName.lastIndexOf(SEPARATOR);
if (sep > 0) {
target = getInternalState(target, fieldName.substring(0, sep));
String prefix = fieldName.substring(0, sep);
Object result = getInternalState(target, prefix);
E.checkArgument(result != null,
"Can't set value on null field: `%s.%s`",
target, prefix);
target = result;
fieldName = fieldName.substring(sep + 1);
}

Expand Down Expand Up @@ -80,6 +86,42 @@ public static <T> T getInternalState(Object target, String fieldName) {
}
}

public static void setInternalFinalState(Object target,
String fieldName, Object value) {
Class<?> c = target instanceof Class<?> ?
(Class<?>) target : target.getClass();
try {
Field field = c.getDeclaredField(fieldName);

// Remove final like FieldUtils.removeFinalModifier()
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int oldModifiers = field.getModifiers();
int nonFinal = oldModifiers & ~Modifier.FINAL;
modifiersField.setInt(field, nonFinal);

// Reset overrideFieldAccessor
Field overrideFAField = Field.class.getDeclaredField("overrideFieldAccessor");
overrideFAField.setAccessible(true);
if (overrideFAField.get(field) != null) {
setInternalState(field, "overrideFieldAccessor", null);
}

try {
field.setAccessible(true);
field.set(target, value);
} finally {
// Resume old modifiers (final)
modifiersField.setInt(field, oldModifiers);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format(
"Can't set value of '%s' against object '%s'",
fieldName, target), e);
}
}

private static Field getFieldFromHierarchy(Class<?> clazz, String field) {
Field f = getField(clazz, field);
while (f == null && clazz != Object.class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public void testSetInternalState() {

Whitebox.setInternalState(test1, "ivalue", 11);
Assert.assertEquals(11, Whitebox.getInternalState(test1, "ivalue"));
Assert.assertEquals(11, test1.ivalue);

Whitebox.setInternalState(test1, "test2.fvalue", 22f);
Assert.assertEquals(22f, Whitebox.getInternalState(test1,
Expand All @@ -97,6 +98,52 @@ public void testSetInternalState() {
});
}

@Test
public void testSetInternalFinalState() {
Test1 test1 = newTest();
Assert.assertEquals(1, test1.ivalueFinal);

Whitebox.setInternalState(test1, "ivalueFinal", 2);
Assert.assertEquals(2, Whitebox.getInternalState(test1, "ivalueFinal"));
// FIXME: seems don't take effect!!!
Assert.assertEquals(1, test1.ivalueFinal);

Whitebox.setInternalFinalState(test1, "ivalueFinal", 3);
Assert.assertEquals(3, Whitebox.getInternalState(test1, "ivalueFinal"));
// FIXME: seems don't take effect!!!
Assert.assertEquals(1, test1.ivalueFinal);
}

@Test
public void testSetInternalStaticFinalState() {
// Assert.assertThrows(RuntimeException.class, () -> {
// Whitebox.setInternalState(Test4.class, "staticFinalValue", 11);
// }, e -> {
// Assert.assertContains("Can't set value of 'staticFinalValue'",
// e.getMessage());
// });

Assert.assertEquals(1, Test4.staticFinalValue);
// Can only call at the first reflect time
Whitebox.setInternalFinalState(Test4.class, "staticFinalValue", 11);
Assert.assertEquals(11, Whitebox.getInternalState(Test4.class,
"staticFinalValue"));

Whitebox.setInternalFinalState(Test4.class, "staticFinalValue", 12);
Assert.assertEquals(12, Whitebox.getInternalState(Test4.class,
"staticFinalValue"));

// FIXME: seems don't take effect!!!
Assert.assertEquals(1, Test4.staticFinalValue);

Assert.assertThrows(RuntimeException.class, () -> {
Whitebox.setInternalState(Test4.class, "staticFinalValue", 1);
}, e -> {
Assert.assertContains("Can't set value of 'staticFinalValue'",
e.getMessage());
});
}

@Test
public void testInvokeStatic() {
Assert.assertEquals(1, Whitebox.invokeStatic(Test1.class, "svalue"));
Expand Down Expand Up @@ -159,7 +206,10 @@ private static Test1 newTest() {
private static class Test1 {

private static int staticValue = 1;

private int ivalue = 1;
private final int ivalueFinal = 1;

private Test2 test2;
private TestSubClass test4;

Expand Down Expand Up @@ -218,6 +268,11 @@ private String value() {
}
}

private static class Test4 {

private static final int staticFinalValue = 1;
}

@SuppressWarnings("unused")
private static class TestSubClass extends Test1 {

Expand Down

0 comments on commit 6b0d97e

Please sign in to comment.