1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 /* 26 * @test 27 * @summary test MethodHandles on value types 28 * @build Point Line MutablePath 29 * @run testng/othervm -XX:+EnableValhalla MethodHandleTest 30 */ 31 32 import java.lang.invoke.*; 33 import java.lang.reflect.Field; 34 import java.util.*; 35 36 import org.testng.annotations.BeforeTest; 37 import org.testng.annotations.DataProvider; 38 import org.testng.annotations.Test; 39 import static org.testng.Assert.*; 40 41 public class MethodHandleTest { 42 private static final Point P = Point.makePoint(10, 20); 43 private static final Line L = Line.makeLine(10, 20, 30, 40); 44 private static final MutablePath PATH = MutablePath.makePath(10, 20, 30, 40); 45 46 @Test 47 public static void testPointClass() throws Throwable { 48 MethodHandleTest test = new MethodHandleTest("Point", P, "x", "y"); 49 test.run(); 50 } 51 52 @Test 53 public static void testLineClass() throws Throwable { 54 MethodHandleTest test = new MethodHandleTest("Line", L, "p1", "p2"); 55 test.run(); 56 } 57 58 @Test 59 public static void testMutablePath() throws Throwable { 60 MethodHandleTest test = new MethodHandleTest("MutablePath", PATH, "p1", "p2"); 61 test.run(); 62 63 // set the mutable fields 64 Point p = Point.makePoint(100, 200); 65 test.setFlattenedField(p); 66 } 67 68 @Test 69 public static void testMixedValues() throws Throwable { 70 MixedValues mv = new MixedValues(P, L, PATH, "mixed", "types"); 71 MethodHandleTest test = new MethodHandleTest("MethodHandleTest$MixedValues", mv, "p", "l", "mutablePath", "list"); 72 test.run(); 73 } 74 75 @Test 76 public static void testArrayElementSetterAndGetter() throws Throwable { 77 testArray(Point[].class, P); 78 testArray(Line[].class, L); 79 testArray(MutablePath[].class, PATH); 80 } 81 82 static void testArray(Class<?> c, Object o) throws Throwable { 83 MethodHandle setter = MethodHandles.arrayElementSetter(c); 84 MethodHandle getter = MethodHandles.arrayElementGetter(c); 85 MethodHandle ctor = MethodHandles.arrayConstructor(c); 86 int size = 5; 87 Object[] array = (Object[])ctor.invoke(size); 88 for (int i=0; i < size; i++) { 89 setter.invoke(array, i, o); 90 } 91 for (int i=0; i < size; i++) { 92 Object v = (Object)getter.invoke(array, i); 93 assertEquals(v, o); 94 } 95 96 // set an array element to null 97 /* 98 Class<?> elementType = c.getComponentType(); 99 try { 100 Object v = (Object)setter.invoke(array, 0, null); 101 assertFalse(elementType.isValue(), "should fail to set a value array element to null"); 102 } catch (NullPointerException e) { 103 assertTrue(elementType.isValue(), "should only fail to set a value array element to null"); 104 } 105 */ 106 } 107 108 private final Class<?> c; 109 private final Object o; 110 private final List<String> names; 111 public MethodHandleTest(String cn, Object o, String... fields) throws Exception { 112 this.c = Class.forName(cn); 113 this.o = o; 114 this.names = List.of(fields); 115 } 116 117 public void run() throws Throwable { 118 for (String name : names) { 119 Field f = c.getDeclaredField(name); 120 unreflectField(f); 121 findGetter(f); 122 varHandle(f); 123 // ensureNonNullable(f); 124 } 125 } 126 127 public List<String> names() { 128 return names; 129 } 130 131 void findGetter(Field f) throws Throwable { 132 MethodHandle mh = MethodHandles.lookup().findGetter(c, f.getName(), f.getType()); 133 Object value = mh.invoke(o); 134 } 135 136 void varHandle(Field f) throws Throwable { 137 VarHandle vh = MethodHandles.lookup().findVarHandle(c, f.getName(), f.getType()); 138 Object value = vh.get(o); 139 } 140 141 void unreflectField(Field f) throws Throwable { 142 MethodHandle mh = MethodHandles.lookup().unreflectGetter(f); 143 Object value = mh.invoke(o); 144 } 145 146 void setFlattenedField(Object value) throws Exception { 147 for (String name : names) { 148 Field f = c.getDeclaredField(name); 149 assertTrue(isFlattened(f)); 150 f.set(o, value); 151 Object nv = f.get(o); 152 assertEquals(nv, value); 153 } 154 } 155 156 void ensureNonNullable(Field f) throws Throwable { 157 boolean nullable = !f.getType().isValue(); 158 try { 159 f.set(o, null); 160 assertTrue(nullable, f + " cannot be set to null"); 161 } catch (NullPointerException e) { 162 assertFalse(nullable, f + " should allow be set to null"); 163 } catch (IllegalAccessException e) { 164 assertTrue(c.isValue()); 165 } 166 try { 167 MethodHandle mh = MethodHandles.lookup().findSetter(c, f.getName(), f.getType()); 168 mh.invoke(o, null); 169 assertTrue(nullable, f + " cannot be set to null"); 170 } catch (NullPointerException e) { 171 assertFalse(nullable, f + " should allow be set to null"); 172 } catch (IllegalAccessException e) { 173 assertTrue(c.isValue()); 174 } 175 try { 176 VarHandle vh = MethodHandles.lookup().findVarHandle(c, f.getName(), f.getType()); 177 vh.set(o, null); 178 assertTrue(nullable, f + " cannot be set to null"); 179 } catch (NullPointerException e) { 180 assertFalse(nullable, f + " should allow be set to null"); 181 } catch (UnsupportedOperationException e) { // TODO: this should be IAE 182 assertTrue(c.isValue()); 183 } 184 } 185 186 187 boolean isFlattened(Field f) { 188 return (f.getModifiers() & 0x00008000) == 0x00008000; 189 } 190 191 static class MixedValues { 192 Point p; 193 Line l; 194 MutablePath mutablePath; 195 List<String> list; 196 197 public MixedValues(Point p, Line l, MutablePath path, String... names) { 198 this.p = p; 199 this.l = l; 200 this.mutablePath = path; 201 this.list = List.of(names); 202 } 203 } 204 205 }