< prev index next >
test/jdk/valhalla/valuetypes/MethodHandleTest.java
Print this page
*** 22,39 ****
*/
/*
* @test
! * @summary test MethodHandles on value types
* @build Point Line MutablePath
* @run testng/othervm -XX:+EnableValhalla MethodHandleTest
*/
import java.lang.invoke.*;
import java.lang.reflect.Field;
import java.util.*;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
--- 22,42 ----
*/
/*
* @test
! * @summary test MethodHandle/VarHandle on value types
* @build Point Line MutablePath
+ * @compile -XDallowFlattenabilityModifiers MethodHandleTest.java
* @run testng/othervm -XX:+EnableValhalla MethodHandleTest
*/
import java.lang.invoke.*;
import java.lang.reflect.Field;
+ import java.lang.reflect.Modifier;
import java.util.*;
+ import java.util.stream.Stream;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
*** 59,77 ****
public static void testMutablePath() throws Throwable {
MethodHandleTest test = new MethodHandleTest("MutablePath", PATH, "p1", "p2");
test.run();
// set the mutable fields
Point p = Point.makePoint(100, 200);
! test.setFlattenedField(p);
}
@Test
public static void testMixedValues() throws Throwable {
MixedValues mv = new MixedValues(P, L, PATH, "mixed", "types");
! MethodHandleTest test = new MethodHandleTest("MethodHandleTest$MixedValues", mv, "p", "l", "mutablePath", "list");
test.run();
}
@Test
public static void testArrayElementSetterAndGetter() throws Throwable {
testArray(Point[].class, P);
--- 62,107 ----
public static void testMutablePath() throws Throwable {
MethodHandleTest test = new MethodHandleTest("MutablePath", PATH, "p1", "p2");
test.run();
// set the mutable fields
+ MutablePath path = MutablePath.makePath(1, 2, 3, 44);
Point p = Point.makePoint(100, 200);
! test.setValueField("p1", path, p);
! test.setValueField("p2", path, p);
! }
!
! @Test
! public static void testValueFields() throws Throwable {
! MutablePath path = MutablePath.makePath(1, 2, 3, 4);
! // p1 and p2 are a non-final field of value type in a reference
! MethodHandleTest test1 = new MethodHandleTest("Point", path.p1, "x", "y");
! test1.run();
!
! MethodHandleTest test2 = new MethodHandleTest("Point", path.p2, "x", "y");
! test2.run();
}
@Test
public static void testMixedValues() throws Throwable {
MixedValues mv = new MixedValues(P, L, PATH, "mixed", "types");
! MethodHandleTest test =
! new MethodHandleTest("MethodHandleTest$MixedValues", mv, "p", "l", "mutablePath", "list", "nfp");
test.run();
+
+ Point p = Point.makePoint(100, 200);
+ Line l = Line.makeLine(100, 200, 300, 400);
+ test.setValueField("p", mv, p);
+ test.setValueField("nfp", mv, p);
+ test.setValueField("l", mv, l);
+ test.setValueField("l", mv, l);
+ test.setValueField("staticPoint", null, p);
+ test.setValueField("staticLine", null, l);
+ // remove the following cases when javac and jvm make
+ // static value fields be flattenable
+ test.setValueField("staticPoint", null, null);
+ test.setValueField("staticLine", null, null);
}
@Test
public static void testArrayElementSetterAndGetter() throws Throwable {
testArray(Point[].class, P);
*** 92,110 ****
Object v = (Object)getter.invoke(array, i);
assertEquals(v, o);
}
// set an array element to null
- /*
Class<?> elementType = c.getComponentType();
try {
Object v = (Object)setter.invoke(array, 0, null);
assertFalse(elementType.isValue(), "should fail to set a value array element to null");
} catch (NullPointerException e) {
assertTrue(elementType.isValue(), "should only fail to set a value array element to null");
}
- */
}
private final Class<?> c;
private final Object o;
private final List<String> names;
--- 122,139 ----
Object v = (Object)getter.invoke(array, i);
assertEquals(v, o);
}
// set an array element to null
Class<?> elementType = c.getComponentType();
try {
+ // value array element is flattenable
Object v = (Object)setter.invoke(array, 0, null);
assertFalse(elementType.isValue(), "should fail to set a value array element to null");
} catch (NullPointerException e) {
assertTrue(elementType.isValue(), "should only fail to set a value array element to null");
}
}
private final Class<?> c;
private final Object o;
private final List<String> names;
*** 118,128 ****
for (String name : names) {
Field f = c.getDeclaredField(name);
unreflectField(f);
findGetter(f);
varHandle(f);
! // ensureNonNullable(f);
}
}
public List<String> names() {
return names;
--- 147,160 ----
for (String name : names) {
Field f = c.getDeclaredField(name);
unreflectField(f);
findGetter(f);
varHandle(f);
! if (c.isValue())
! ensureImmutable(f);
! else
! ensureNullable(f);
}
}
public List<String> names() {
return names;
*** 141,205 ****
void unreflectField(Field f) throws Throwable {
MethodHandle mh = MethodHandles.lookup().unreflectGetter(f);
Object value = mh.invoke(o);
}
! void setFlattenedField(Object value) throws Exception {
! for (String name : names) {
Field f = c.getDeclaredField(name);
! assertTrue(isFlattened(f));
! f.set(o, value);
! Object nv = f.get(o);
! assertEquals(nv, value);
}
}
! void ensureNonNullable(Field f) throws Throwable {
! boolean nullable = !f.getType().isValue();
try {
f.set(o, null);
assertTrue(nullable, f + " cannot be set to null");
} catch (NullPointerException e) {
assertFalse(nullable, f + " should allow be set to null");
- } catch (IllegalAccessException e) {
- assertTrue(c.isValue());
}
try {
MethodHandle mh = MethodHandles.lookup().findSetter(c, f.getName(), f.getType());
mh.invoke(o, null);
assertTrue(nullable, f + " cannot be set to null");
} catch (NullPointerException e) {
assertFalse(nullable, f + " should allow be set to null");
- } catch (IllegalAccessException e) {
- assertTrue(c.isValue());
}
try {
VarHandle vh = MethodHandles.lookup().findVarHandle(c, f.getName(), f.getType());
vh.set(o, null);
assertTrue(nullable, f + " cannot be set to null");
} catch (NullPointerException e) {
assertFalse(nullable, f + " should allow be set to null");
- } catch (UnsupportedOperationException e) { // TODO: this should be IAE
- assertTrue(c.isValue());
}
}
boolean isFlattened(Field f) {
return (f.getModifiers() & 0x00008000) == 0x00008000;
}
static class MixedValues {
Point p;
Line l;
MutablePath mutablePath;
List<String> list;
public MixedValues(Point p, Line l, MutablePath path, String... names) {
this.p = p;
this.l = l;
this.mutablePath = path;
this.list = List.of(names);
}
}
}
--- 173,317 ----
void unreflectField(Field f) throws Throwable {
MethodHandle mh = MethodHandles.lookup().unreflectGetter(f);
Object value = mh.invoke(o);
}
! /*
! * Test setting value field to a new value.
! * The field must be flattenable but may or may not be flattened.
! */
! void setValueField(String name, Object obj, Object value) throws Throwable {
Field f = c.getDeclaredField(name);
! boolean isStatic = Modifier.isStatic(f.getModifiers());
! assertTrue(f.getType().isValue());
! assertTrue((isStatic && obj == null) || (!isStatic && obj != null));
! Object v = f.get(obj);
!
! // Field::set
! try {
! f.set(obj, value);
! assertEquals(f.get(obj), value);
! } finally {
! f.set(obj, v);
! }
!
!
! if (isStatic) {
! // MethodHandle::invoke
! try {
! MethodHandle mh = MethodHandles.lookup().findStaticSetter(c, f.getName(), f.getType());
! mh.invoke(f.getType().cast(value));
! assertEquals(f.get(obj), value);
! } finally {
! f.set(obj, v);
! }
! // VarHandle::set
! try {
! VarHandle vh = MethodHandles.lookup().findStaticVarHandle(c, f.getName(), f.getType());
! vh.set(f.getType().cast(value));
! assertEquals(f.get(obj), value);
! } finally {
! f.set(obj, v);
! }
! } else {
! // MethodHandle::invoke
! try {
! MethodHandle mh = MethodHandles.lookup().findSetter(c, f.getName(), f.getType());
! mh.invoke(obj, value);
! assertEquals(f.get(obj), value);
! } finally {
! f.set(obj, v);
! }
! // VarHandle::set
! try {
! VarHandle vh = MethodHandles.lookup().findVarHandle(c, f.getName(), f.getType());
! vh.set(obj, value);
! assertEquals(f.get(obj), value);
! } finally {
! f.set(obj, v);
! }
}
}
! /*
! * Test setting the given field to null via reflection, method handle
! * and var handle.
! */
! void ensureNullable(Field f) throws Throwable {
! assertFalse(Modifier.isStatic(f.getModifiers()));
! // flattenable implies non-nullable
! boolean nullable = !isFlattenable(f);
! // test reflection
try {
f.set(o, null);
assertTrue(nullable, f + " cannot be set to null");
} catch (NullPointerException e) {
assertFalse(nullable, f + " should allow be set to null");
}
+ // test method handle, i.e. putfield bytecode behavior
try {
MethodHandle mh = MethodHandles.lookup().findSetter(c, f.getName(), f.getType());
mh.invoke(o, null);
assertTrue(nullable, f + " cannot be set to null");
} catch (NullPointerException e) {
assertFalse(nullable, f + " should allow be set to null");
}
+ // test var handle
try {
VarHandle vh = MethodHandles.lookup().findVarHandle(c, f.getName(), f.getType());
vh.set(o, null);
assertTrue(nullable, f + " cannot be set to null");
} catch (NullPointerException e) {
assertFalse(nullable, f + " should allow be set to null");
}
}
+ void ensureImmutable(Field f) throws Throwable {
+ assertFalse(Modifier.isStatic(f.getModifiers()));
+ Object v = f.get(o);
+ // test reflection
+ try {
+ f.set(o, v);
+ throw new RuntimeException(f + " should be immutable");
+ } catch (IllegalAccessException e) {}
+
+ // test method handle, i.e. putfield bytecode behavior
+ try {
+ MethodHandle mh = MethodHandles.lookup().findSetter(c, f.getName(), f.getType());
+ mh.invoke(o, v);
+ throw new RuntimeException(f + " should be immutable");
+ } catch (IllegalAccessException e) { }
+ // test var handle
+ try {
+ VarHandle vh = MethodHandles.lookup().findVarHandle(c, f.getName(), f.getType());
+ vh.set(o, v);
+ throw new RuntimeException(f + " should be immutable");
+ } catch (UnsupportedOperationException e) {}
+ }
boolean isFlattened(Field f) {
return (f.getModifiers() & 0x00008000) == 0x00008000;
}
+ boolean isFlattenable(Field f) {
+ return (f.getModifiers() & 0x00000100) == 0x00000100;
+ }
+
static class MixedValues {
+ static Point staticPoint = Point.makePoint(10, 10);
+ static Line staticLine; // LW1 allows null static value field
Point p;
Line l;
MutablePath mutablePath;
List<String> list;
+ __NotFlattened Point nfp;
public MixedValues(Point p, Line l, MutablePath path, String... names) {
this.p = p;
this.l = l;
this.mutablePath = path;
this.list = List.of(names);
+ this.nfp = p;
}
}
}
< prev index next >