8220282: Add MethodHandle tests on accessing final fields
Reviewed-by: lancea
This commit is contained in:
parent
0abdc381b7
commit
ab361746ec
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -635,7 +635,7 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
|
|
||||||
public void testGetter(int testMode) throws Throwable {
|
public void testGetter(int testMode) throws Throwable {
|
||||||
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
|
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
|
||||||
for (Object[] c : HasFields.CASES) {
|
for (Object[] c : HasFields.testCasesFor(testMode)) {
|
||||||
boolean positive = (c[1] != Error.class);
|
boolean positive = (c[1] != Error.class);
|
||||||
testGetter(positive, lookup, c[0], c[1], testMode);
|
testGetter(positive, lookup, c[0], c[1], testMode);
|
||||||
if (positive)
|
if (positive)
|
||||||
@ -665,7 +665,6 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
boolean testNPE = ((testMode0 & TEST_NPE) != 0);
|
boolean testNPE = ((testMode0 & TEST_NPE) != 0);
|
||||||
int testMode = testMode0 & ~(TEST_SETTER | TEST_BOUND | TEST_NPE);
|
int testMode = testMode0 & ~(TEST_SETTER | TEST_BOUND | TEST_NPE);
|
||||||
boolean positive = positive0 && !testNPE;
|
boolean positive = positive0 && !testNPE;
|
||||||
boolean isFinal;
|
|
||||||
boolean isStatic;
|
boolean isStatic;
|
||||||
Class<?> fclass;
|
Class<?> fclass;
|
||||||
String fname;
|
String fname;
|
||||||
@ -673,14 +672,12 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
Field f = (fieldRef instanceof Field ? (Field)fieldRef : null);
|
Field f = (fieldRef instanceof Field ? (Field)fieldRef : null);
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
isStatic = Modifier.isStatic(f.getModifiers());
|
isStatic = Modifier.isStatic(f.getModifiers());
|
||||||
isFinal = Modifier.isFinal(f.getModifiers());
|
|
||||||
fclass = f.getDeclaringClass();
|
fclass = f.getDeclaringClass();
|
||||||
fname = f.getName();
|
fname = f.getName();
|
||||||
ftype = f.getType();
|
ftype = f.getType();
|
||||||
} else {
|
} else {
|
||||||
Object[] scnt = (Object[]) fieldRef;
|
Object[] scnt = (Object[]) fieldRef;
|
||||||
isStatic = (Boolean) scnt[0];
|
isStatic = (Boolean) scnt[0];
|
||||||
isFinal = false;
|
|
||||||
fclass = (Class<?>) scnt[1];
|
fclass = (Class<?>) scnt[1];
|
||||||
fname = (String) scnt[2];
|
fname = (String) scnt[2];
|
||||||
ftype = (Class<?>) scnt[3];
|
ftype = (Class<?>) scnt[3];
|
||||||
@ -724,19 +721,21 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
? NoSuchFieldException.class
|
? NoSuchFieldException.class
|
||||||
: IllegalAccessException.class,
|
: IllegalAccessException.class,
|
||||||
noAccess);
|
noAccess);
|
||||||
if (((testMode0 & TEST_SETTER) != 0) && (isFinal && isStatic)) return; // Final static field setter test failed as intended.
|
|
||||||
if (verbosity >= 5) ex.printStackTrace(System.out);
|
if (verbosity >= 5) ex.printStackTrace(System.out);
|
||||||
}
|
}
|
||||||
if (verbosity >= 3)
|
if (verbosity >= 3)
|
||||||
System.out.println((((testMode0 & TEST_UNREFLECT) != 0)?"unreflect":"find")
|
System.out.format("%s%s %s.%s/%s => %s %s%n",
|
||||||
+(((testMode0 & TEST_FIND_STATIC) != 0)?"Static":"")
|
(testMode0 & TEST_UNREFLECT) != 0
|
||||||
+(isGetter?"Getter":"Setter")
|
? "unreflect"
|
||||||
+" "+fclass.getName()+"."+fname+"/"+ftype
|
: "find" + ((testMode0 & TEST_FIND_STATIC) != 0 ? "Static" : ""),
|
||||||
+" => "+mh
|
(isGetter ? "Getter" : "Setter"),
|
||||||
+(noAccess == null ? "" : " !! "+noAccess));
|
fclass.getName(), fname, ftype, mh,
|
||||||
|
(noAccess == null ? "" : " !! "+noAccess));
|
||||||
|
// negative test case and expected noAccess, then done.
|
||||||
|
if (!positive && noAccess != null) return;
|
||||||
|
// positive test case but found noAccess, then error
|
||||||
if (positive && !testNPE && noAccess != null) throw new RuntimeException(noAccess);
|
if (positive && !testNPE && noAccess != null) throw new RuntimeException(noAccess);
|
||||||
assertEquals(positive0 ? "positive test" : "negative test erroneously passed", positive0, mh != null);
|
assertEquals(positive0 ? "positive test" : "negative test erroneously passed", positive0, mh != null);
|
||||||
assertFalse("Setter methods should throw an exception if passed a final static field.", ((testMode0 & TEST_SETTER) != 0) && (isFinal && isStatic));
|
|
||||||
if (!positive && !testNPE) return; // negative access test failed as expected
|
if (!positive && !testNPE) return; // negative access test failed as expected
|
||||||
assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
|
assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
|
||||||
assertSame(mh.type(), expType);
|
assertSame(mh.type(), expType);
|
||||||
@ -762,6 +761,9 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
assertEquals(f.get(fields), value); // clean to start with
|
assertEquals(f.get(fields), value); // clean to start with
|
||||||
}
|
}
|
||||||
Throwable caughtEx = null;
|
Throwable caughtEx = null;
|
||||||
|
// non-final field and setAccessible(true) on instance field will have write access
|
||||||
|
boolean writeAccess = !Modifier.isFinal(f.getModifiers()) ||
|
||||||
|
(!Modifier.isStatic(f.getModifiers()) && f.isAccessible());
|
||||||
if (isGetter) {
|
if (isGetter) {
|
||||||
Object expValue = value;
|
Object expValue = value;
|
||||||
for (int i = 0; i <= 1; i++) {
|
for (int i = 0; i <= 1; i++) {
|
||||||
@ -785,7 +787,7 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertEquals(sawValue, expValue);
|
assertEquals(sawValue, expValue);
|
||||||
if (f != null && f.getDeclaringClass() == HasFields.class && !isFinal) {
|
if (f != null && f.getDeclaringClass() == HasFields.class && writeAccess) {
|
||||||
Object random = randomArg(ftype);
|
Object random = randomArg(ftype);
|
||||||
f.set(fields, random);
|
f.set(fields, random);
|
||||||
expValue = random;
|
expValue = random;
|
||||||
@ -819,8 +821,8 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((f != null) && (f.getDeclaringClass() == HasFields.class) && !isFinal) {
|
if (f != null && f.getDeclaringClass() == HasFields.class && writeAccess) {
|
||||||
f.set(fields, value); // put it back if we changed it.
|
f.set(fields, value); // put it back if it has write access
|
||||||
}
|
}
|
||||||
if (testNPE) {
|
if (testNPE) {
|
||||||
if (caughtEx == null || !(caughtEx instanceof NullPointerException))
|
if (caughtEx == null || !(caughtEx instanceof NullPointerException))
|
||||||
@ -868,7 +870,8 @@ public class MethodHandlesGeneralTest extends MethodHandlesTest {
|
|||||||
|
|
||||||
public void testSetter(int testMode) throws Throwable {
|
public void testSetter(int testMode) throws Throwable {
|
||||||
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
|
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
|
||||||
for (Object[] c : HasFields.CASES) {
|
startTest("testSetter");
|
||||||
|
for (Object[] c : HasFields.testCasesFor(testMode|TEST_SETTER)) {
|
||||||
boolean positive = (c[1] != Error.class);
|
boolean positive = (c[1] != Error.class);
|
||||||
testSetter(positive, lookup, c[0], c[1], testMode);
|
testSetter(positive, lookup, c[0], c[1], testMode);
|
||||||
if (positive)
|
if (positive)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -38,6 +38,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@ -561,25 +562,37 @@ public abstract class MethodHandlesTest {
|
|||||||
static long sJ = 1+'J';
|
static long sJ = 1+'J';
|
||||||
static float sF = 1+'F';
|
static float sF = 1+'F';
|
||||||
static double sD = 1+'D';
|
static double sD = 1+'D';
|
||||||
|
|
||||||
|
// final fields
|
||||||
|
final boolean fiZ = false;
|
||||||
|
final byte fiB = 2+(byte)'B';
|
||||||
|
final short fiS = 2+(short)'S';
|
||||||
|
final char fiC = 2+'C';
|
||||||
|
final int fiI = 2+'I';
|
||||||
|
final long fiJ = 2+'J';
|
||||||
|
final float fiF = 2+'F';
|
||||||
|
final double fiD = 2+'D';
|
||||||
final static boolean fsZ = false;
|
final static boolean fsZ = false;
|
||||||
final static byte fsB = 2+(byte)'B';
|
final static byte fsB = 3+(byte)'B';
|
||||||
final static short fsS = 2+(short)'S';
|
final static short fsS = 3+(short)'S';
|
||||||
final static char fsC = 2+'C';
|
final static char fsC = 3+'C';
|
||||||
final static int fsI = 2+'I';
|
final static int fsI = 3+'I';
|
||||||
final static long fsJ = 2+'J';
|
final static long fsJ = 3+'J';
|
||||||
final static float fsF = 2+'F';
|
final static float fsF = 3+'F';
|
||||||
final static double fsD = 2+'D';
|
final static double fsD = 3+'D';
|
||||||
|
|
||||||
Object iL = 'L';
|
Object iL = 'L';
|
||||||
String iR = "R";
|
String iR = "iR";
|
||||||
static Object sL = 'M';
|
static Object sL = 1+'L';
|
||||||
static String sR = "S";
|
static String sR = "sR";
|
||||||
final static Object fsL = 'N';
|
final Object fiL = 2+'L';
|
||||||
final static String fsR = "T";
|
final String fiR = "fiR";
|
||||||
|
final static Object fsL = 3+'L';
|
||||||
|
final static String fsR = "fsR";
|
||||||
|
|
||||||
static final Object[][] CASES;
|
static final ArrayList<Object[]> STATIC_FIELD_CASES = new ArrayList<>();
|
||||||
|
static final ArrayList<Object[]> INSTANCE_FIELD_CASES = new ArrayList<>();
|
||||||
static {
|
static {
|
||||||
ArrayList<Object[]> cases = new ArrayList<>();
|
|
||||||
Object types[][] = {
|
Object types[][] = {
|
||||||
{'L',Object.class}, {'R',String.class},
|
{'L',Object.class}, {'R',String.class},
|
||||||
{'I',int.class}, {'J',long.class},
|
{'I',int.class}, {'J',long.class},
|
||||||
@ -589,42 +602,91 @@ public abstract class MethodHandlesTest {
|
|||||||
};
|
};
|
||||||
HasFields fields = new HasFields();
|
HasFields fields = new HasFields();
|
||||||
for (Object[] t : types) {
|
for (Object[] t : types) {
|
||||||
for (int kind = 0; kind <= 2; kind++) {
|
for (int kind = 0; kind <= 1; kind++) {
|
||||||
boolean isStatic = (kind != 0);
|
boolean isStatic = (kind != 0);
|
||||||
boolean isFinal = (kind == 2);
|
ArrayList<Object[]> cases = isStatic ? STATIC_FIELD_CASES : INSTANCE_FIELD_CASES;
|
||||||
char btc = (Character)t[0];
|
char btc = (Character)t[0];
|
||||||
String name = (isStatic ? "s" : "i") + btc;
|
String fname = (isStatic ? "s" : "i") + btc;
|
||||||
if (isFinal) name = "f" + name;
|
String finalFname = (isStatic ? "fs" : "fi") + btc;
|
||||||
Class<?> type = (Class<?>) t[1];
|
Class<?> type = (Class<?>) t[1];
|
||||||
Object value;
|
// non-final field
|
||||||
Field field;
|
Field nonFinalField = getField(fname, type);
|
||||||
try {
|
Object value = getValue(fields, nonFinalField);
|
||||||
field = HasFields.class.getDeclaredField(name);
|
|
||||||
} catch (NoSuchFieldException | SecurityException ex) {
|
|
||||||
throw new InternalError("no field HasFields."+name);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
value = field.get(fields);
|
|
||||||
} catch (IllegalArgumentException | IllegalAccessException ex) {
|
|
||||||
throw new InternalError("cannot fetch field HasFields."+name);
|
|
||||||
}
|
|
||||||
if (type == float.class) {
|
if (type == float.class) {
|
||||||
float v = 'F';
|
float v = 'F';
|
||||||
if (isStatic) v++;
|
if (isStatic) v++;
|
||||||
if (isFinal) v++;
|
|
||||||
assertTrue(value.equals(v));
|
assertTrue(value.equals(v));
|
||||||
}
|
}
|
||||||
if (isFinal && isStatic) field.setAccessible(true);
|
assertTrue(isStatic == (Modifier.isStatic(nonFinalField.getModifiers())));
|
||||||
assertTrue(name.equals(field.getName()));
|
cases.add(new Object[]{ nonFinalField, value });
|
||||||
assertTrue(type.equals(field.getType()));
|
|
||||||
assertTrue(isStatic == (Modifier.isStatic(field.getModifiers())));
|
// setAccessible(true) on final field but static final field only has read access
|
||||||
assertTrue(isFinal == (Modifier.isFinal(field.getModifiers())));
|
Field finalField = getField(finalFname, type);
|
||||||
cases.add(new Object[]{ field, value });
|
Object fvalue = getValue(fields, finalField);
|
||||||
|
finalField.setAccessible(true);
|
||||||
|
assertTrue(isStatic == (Modifier.isStatic(finalField.getModifiers())));
|
||||||
|
cases.add(new Object[]{ finalField, fvalue, Error.class});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cases.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class });
|
INSTANCE_FIELD_CASES.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class });
|
||||||
cases.add(new Object[]{ new Object[]{ true, HasFields.class, "bogus_sL", Object.class }, Error.class });
|
STATIC_FIELD_CASES.add(new Object[]{ new Object[]{ true, HasFields.class, "bogus_sL", Object.class }, Error.class });
|
||||||
CASES = cases.toArray(new Object[0][]);
|
}
|
||||||
|
|
||||||
|
private static Field getField(String name, Class<?> type) {
|
||||||
|
try {
|
||||||
|
Field field = HasFields.class.getDeclaredField(name);
|
||||||
|
assertTrue(name.equals(field.getName()));
|
||||||
|
assertTrue(type.equals(field.getType()));
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException | SecurityException ex) {
|
||||||
|
throw new InternalError("no field HasFields."+name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getValue(Object o, Field field) {
|
||||||
|
try {
|
||||||
|
return field.get(o);
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException ex) {
|
||||||
|
throw new InternalError("cannot fetch field HasFields."+field.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object[][] testCasesFor(int testMode) {
|
||||||
|
Stream<Object[]> cases;
|
||||||
|
if ((testMode & TEST_UNREFLECT) != 0) {
|
||||||
|
cases = Stream.concat(STATIC_FIELD_CASES.stream(), INSTANCE_FIELD_CASES.stream());
|
||||||
|
} else if ((testMode & TEST_FIND_STATIC) != 0) {
|
||||||
|
cases = STATIC_FIELD_CASES.stream();
|
||||||
|
} else if ((testMode & TEST_FIND_FIELD) != 0) {
|
||||||
|
cases = INSTANCE_FIELD_CASES.stream();
|
||||||
|
} else {
|
||||||
|
throw new InternalError("unexpected test mode: " + testMode);
|
||||||
|
}
|
||||||
|
return cases.map(c -> mapTestCase(testMode, c)).toArray(Object[][]::new);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object[] mapTestCase(int testMode, Object[] c) {
|
||||||
|
// non-final fields (2-element) and final fields (3-element) if not TEST_SETTER
|
||||||
|
if (c.length == 2 || (testMode & TEST_SETTER) == 0)
|
||||||
|
return c;
|
||||||
|
|
||||||
|
// final fields (3-element)
|
||||||
|
assertTrue((testMode & TEST_SETTER) != 0 && c[0] instanceof Field && c[2] == Error.class);
|
||||||
|
if ((testMode & TEST_UNREFLECT) == 0)
|
||||||
|
return new Object[]{ c[0], c[2]}; // negative test case; can't set on final fields
|
||||||
|
|
||||||
|
// unreflectSetter grants write access on instance final field if accessible flag is true
|
||||||
|
// hence promote the negative test case to positive test case
|
||||||
|
Field f = (Field) c[0];
|
||||||
|
int mods = f.getModifiers();
|
||||||
|
if (!Modifier.isFinal(mods) || (!Modifier.isStatic(mods) && f.isAccessible())) {
|
||||||
|
// positive test case
|
||||||
|
return new Object[]{ c[0], c[1] };
|
||||||
|
} else {
|
||||||
|
// otherwise, negative test case
|
||||||
|
return new Object[]{ c[0], c[2]};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user