1 /* 2 * Copyright (c) 2020, 2023, 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 * @test 26 * @enablePreview 27 * @modules java.base/jdk.internal.access.foreign 28 * @modules java.base/jdk.internal.foreign.layout 29 * 30 * @run testng/othervm -Xverify:all 31 * -Djdk.internal.foreign.SHOULD_ADAPT_HANDLES=false 32 * VarHandleTestExact 33 * @run testng/othervm -Xverify:all 34 * -Djdk.internal.foreign.SHOULD_ADAPT_HANDLES=false 35 * -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true 36 * -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true 37 * VarHandleTestExact 38 * @run testng/othervm -Xverify:all 39 * -Djdk.internal.foreign.SHOULD_ADAPT_HANDLES=false 40 * -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false 41 * -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false 42 * VarHandleTestExact 43 * @run testng/othervm -Xverify:all 44 * -Djdk.internal.foreign.SHOULD_ADAPT_HANDLES=false 45 * -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false 46 * -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true 47 * VarHandleTestExact 48 */ 49 50 import java.lang.foreign.Arena; 51 import java.lang.foreign.MemorySegment; 52 53 import jdk.internal.foreign.layout.ValueLayouts; 54 import org.testng.SkipException; 55 import org.testng.annotations.DataProvider; 56 import org.testng.annotations.Test; 57 58 import java.lang.invoke.MethodHandles; 59 import java.lang.invoke.VarHandle; 60 import java.lang.invoke.WrongMethodTypeException; 61 import java.lang.reflect.Array; 62 import java.nio.ByteBuffer; 63 import java.nio.ByteOrder; 64 import java.util.ArrayList; 65 import java.util.List; 66 import java.util.function.Consumer; 67 68 import static org.testng.Assert.*; 69 70 public class VarHandleTestExact { 71 72 private static class Widget { 73 static Object objectField_SRW; 74 static long longField_SRW; 75 static double doubleField_SRW; 76 static Long aLongField_SRW; 77 78 Object objectField_RW; 79 long longField_RW; 80 double doubleField_RW; 81 Long aLongField_RW; 82 83 final static Object objectField_SRO = new Object(); 84 final static long longField_SRO = 1234L; 85 final static double doubleField_SRO = 1234D; 86 final static Long aLongField_SRO = 1234L; 87 88 final Object objectField_RO = new Object(); 89 final long longField_RO = 1234L; 90 final double doubleField_RO = 1234D; 91 final Long aLongField_RO = 1234L; 92 } 93 94 @Test(dataProvider = "dataObjectAccess") 95 public void testExactSet(String fieldBaseName, Class<?> fieldType, boolean ro, Object testValue, 96 SetX setter, GetX getter, 97 SetStaticX staticSetter, GetStaticX staticGetter) 98 throws NoSuchFieldException, IllegalAccessException { 99 if (ro) throw new SkipException("Can not test setter with read only field"); 100 VarHandle vh = MethodHandles.lookup().findVarHandle(Widget.class, fieldBaseName + "_RW", fieldType); 101 Widget w = new Widget(); 102 103 doTest(vh, 104 tvh -> tvh.set(w, testValue), 105 tvh -> setter.set(tvh, w, testValue), 106 ".*\\Qhandle's method type (Widget," + fieldType.getSimpleName() + ")void \\E.*"); 107 } 108 109 @Test(dataProvider = "dataObjectAccess") 110 public void testExactGet(String fieldBaseName, Class<?> fieldType, boolean ro, Object testValue, 111 SetX setter, GetX getter, 112 SetStaticX staticSetter, GetStaticX staticGetter) 113 throws NoSuchFieldException, IllegalAccessException { 114 VarHandle vh = MethodHandles.lookup().findVarHandle(Widget.class, fieldBaseName + (ro ? "_RO" : "_RW"), fieldType); 115 Widget w = new Widget(); 116 117 doTest(vh, 118 tvh -> tvh.get(w), 119 tvh -> getter.get(tvh, w), 120 ".*\\Qhandle's method type (Widget)" + fieldType.getSimpleName() + " \\E.*"); 121 } 122 123 @Test(dataProvider = "dataObjectAccess") 124 public void testExactSetStatic(String fieldBaseName, Class<?> fieldType, boolean ro, Object testValue, 125 SetX setter, GetX getter, 126 SetStaticX staticSetter, GetStaticX staticGetter) 127 throws NoSuchFieldException, IllegalAccessException { 128 if (ro) throw new SkipException("Can not test setter with read only field"); 129 VarHandle vh = MethodHandles.lookup().findStaticVarHandle(Widget.class, fieldBaseName + "_SRW", fieldType); 130 131 doTest(vh, 132 tvh -> tvh.set(testValue), 133 tvh -> staticSetter.set(tvh, testValue), 134 ".*\\Qhandle's method type (" + fieldType.getSimpleName() + ")void \\E.*"); 135 } 136 137 @Test(dataProvider = "dataObjectAccess") 138 public void testExactGetStatic(String fieldBaseName, Class<?> fieldType, boolean ro, Object testValue, 139 SetX setter, GetX getter, 140 SetStaticX staticSetter, GetStaticX staticGetter) 141 throws NoSuchFieldException, IllegalAccessException { 142 VarHandle vh = MethodHandles.lookup().findStaticVarHandle(Widget.class, fieldBaseName + (ro ? "_SRO" : "_SRW"), fieldType); 143 144 doTest(vh, 145 tvh -> tvh.get(), 146 tvh -> staticGetter.get(tvh), 147 ".*\\Qhandle's method type ()" + fieldType.getSimpleName() + " \\E.*"); 148 } 149 150 @Test(dataProvider = "dataSetArray") 151 public void testExactArraySet(Class<?> arrayClass, Object testValue, SetArrayX setter) { 152 VarHandle vh = MethodHandles.arrayElementVarHandle(arrayClass); 153 Object arr = Array.newInstance(arrayClass.componentType(), 1); 154 155 doTest(vh, 156 tvh -> tvh.set(arr, 0, testValue), 157 tvh -> setter.set(tvh, arr, testValue), 158 ".*\\Qhandle's method type (" + arrayClass.getSimpleName() + ",int," + arrayClass.componentType().getSimpleName() + ")void \\E.*"); 159 } 160 161 @Test(dataProvider = "dataSetBuffer") 162 public void testExactBufferSet(Class<?> arrayClass, Object testValue, SetBufferX setter) { 163 VarHandle vh = MethodHandles.byteBufferViewVarHandle(arrayClass, ByteOrder.nativeOrder()); 164 ByteBuffer buff = ByteBuffer.allocateDirect(8); 165 166 doTest(vh, 167 tvh -> tvh.set(buff, 0, testValue), 168 tvh -> setter.set(tvh, buff, testValue), 169 ".*\\Qhandle's method type (ByteBuffer,int," + arrayClass.componentType().getSimpleName() + ")void \\E.*"); 170 } 171 172 @Test(dataProvider = "dataSetMemorySegment") 173 public void testExactSegmentSet(Class<?> carrier, Object testValue, SetSegmentX setter) { 174 VarHandle vh = MethodHandles.memorySegmentViewVarHandle(ValueLayouts.valueLayout(carrier, ByteOrder.nativeOrder())); 175 try (Arena arena = Arena.ofConfined()) { 176 MemorySegment seg = arena.allocate(8); 177 doTest(vh, 178 tvh -> tvh.set(seg, 0L, testValue), 179 tvh -> setter.set(tvh, seg, 0L, testValue), 180 ".*\\Qhandle's method type (MemorySegment,long," + carrier.getSimpleName() + ")void \\E.*"); 181 } 182 } 183 184 private static void doTest(VarHandle invokeHandle, Consumer<VarHandle> invokeTest, 185 Consumer<VarHandle> invokeExactTest, String expectedMessage) { 186 assertFalse(invokeHandle.hasInvokeExactBehavior()); 187 assertSame(invokeHandle, invokeHandle.withInvokeBehavior()); 188 try { 189 invokeTest.accept(invokeHandle); 190 } catch (WrongMethodTypeException wmte) { 191 fail("Unexpected exception", wmte); 192 } 193 194 VarHandle invokeExactHandle = invokeHandle.withInvokeExactBehavior(); 195 assertTrue(invokeExactHandle.hasInvokeExactBehavior()); 196 assertSame(invokeExactHandle, invokeExactHandle.withInvokeExactBehavior()); 197 try { 198 invokeExactTest.accept(invokeExactHandle); // should throw 199 fail("Exception expected"); 200 } catch (WrongMethodTypeException wmte) { 201 assertMatches(wmte.getMessage(), expectedMessage); 202 } 203 204 // try going back 205 VarHandle invokeHandle2 = invokeExactHandle.withInvokeBehavior(); 206 assertFalse(invokeHandle2.hasInvokeExactBehavior()); 207 try { 208 invokeTest.accept(invokeHandle2); 209 } catch (WrongMethodTypeException wmte) { 210 fail("Unexpected exception", wmte); 211 } 212 } 213 214 private static void assertMatches(String str, String pattern) { 215 if (!str.matches(pattern)) { 216 throw new AssertionError("'" + str + "' did not match the pattern '" + pattern + "'."); 217 } 218 } 219 220 private interface SetX { 221 void set(VarHandle vh, Widget w, Object testValue); 222 } 223 224 private interface SetStaticX { 225 void set(VarHandle vh, Object testValue); 226 } 227 228 private interface GetX { 229 void get(VarHandle vh, Widget w); 230 } 231 232 private interface GetStaticX { 233 void get(VarHandle vh); 234 } 235 236 private interface SetArrayX { 237 void set(VarHandle vh, Object array, Object testValue); 238 } 239 240 private interface SetBufferX { 241 void set(VarHandle vh, ByteBuffer buff, Object testValue); 242 } 243 244 private interface SetSegmentX { 245 void set(VarHandle vh, MemorySegment segment, long offset, Object testValue); 246 } 247 248 private static void consume(Object o) {} 249 250 private static void testCaseObjectAccess(List<Object[]> cases, String fieldBaseName, Class<?> fieldType, Object testValue, 251 SetX setter, GetX getter, 252 SetStaticX staticSetter, GetStaticX staticGetter) { 253 cases.add(new Object[] { fieldBaseName, fieldType, false, testValue, setter, getter, staticSetter, staticGetter }); 254 cases.add(new Object[] { fieldBaseName, fieldType, true, testValue, setter, getter, staticSetter, staticGetter }); 255 } 256 257 private static void testCaseArraySet(List<Object[]> cases, Class<?> arrayType, Object testValue, SetArrayX setter) { 258 cases.add(new Object[] { arrayType, testValue, setter }); 259 } 260 261 private static void testCaseBufferSet(List<Object[]> cases, Class<?> arrayType, Object testValue, SetBufferX setter) { 262 cases.add(new Object[] { arrayType, testValue, setter }); 263 } 264 265 private static void testCaseSegmentSet(List<Object[]> cases, Class<?> carrier, Object testValue, SetSegmentX setter) { 266 cases.add(new Object[] { carrier, testValue, setter }); 267 } 268 269 @DataProvider 270 public static Object[][] dataObjectAccess() { 271 List<Object[]> cases = new ArrayList<>(); 272 273 // create a bunch of different sig-poly call sites 274 testCaseObjectAccess(cases, "objectField", Object.class, "abcd", 275 (vh, w, tv) -> vh.set(w, (String) tv), 276 (vh, w) -> consume((String) vh.get(w)), 277 (vh, tv) -> vh.set((String) tv), 278 (vh) -> consume((String) vh.get())); 279 testCaseObjectAccess(cases, "objectField", Object.class, Integer.valueOf(1234), 280 (vh, w, tv) -> vh.set(w, (Integer) tv), 281 (vh, w) -> consume((Integer) vh.get(w)), 282 (vh, tv) -> vh.set((Integer) tv), 283 (vh) -> consume((Integer) vh.get())); 284 testCaseObjectAccess(cases, "longField", long.class, 1234, 285 (vh, w, tv) -> vh.set(w, (int) tv), 286 (vh, w) -> consume((int) vh.get(w)), 287 (vh, tv) -> vh.set((int) tv), 288 (vh) -> consume((int) vh.get())); 289 testCaseObjectAccess(cases, "longField", long.class, (short) 1234, 290 (vh, w, tv) -> vh.set(w, (short) tv), 291 (vh, w) -> consume((short) vh.get(w)), 292 (vh, tv) -> vh.set((short) tv), 293 (vh) -> consume((short) vh.get())); 294 testCaseObjectAccess(cases, "longField", long.class, (char) 1234, 295 (vh, w, tv) -> vh.set(w, (char) tv), 296 (vh, w) -> consume((char) vh.get(w)), 297 (vh, tv) -> vh.set((char) tv), 298 (vh) -> consume((char) vh.get())); 299 testCaseObjectAccess(cases, "longField", long.class, (byte) 1234, 300 (vh, w, tv) -> vh.set(w, (byte) tv), 301 (vh, w) -> consume((byte) vh.get(w)), 302 (vh, tv) -> vh.set((byte) tv), 303 (vh) -> consume((byte) vh.get())); 304 testCaseObjectAccess(cases, "longField", long.class, Long.valueOf(1234L), 305 (vh, w, tv) -> vh.set(w, (Long) tv), 306 (vh, w) -> consume((Long) vh.get(w)), 307 (vh, tv) -> vh.set((Long) tv), 308 (vh) -> consume((Long) vh.get())); 309 testCaseObjectAccess(cases, "doubleField", double.class, 1234F, 310 (vh, w, tv) -> vh.set(w, (float) tv), 311 (vh, w) -> consume((float) vh.get(w)), 312 (vh, tv) -> vh.set((float) tv), 313 (vh) -> consume((float) vh.get())); 314 testCaseObjectAccess(cases, "doubleField", double.class, 1234, 315 (vh, w, tv) -> vh.set(w, (int) tv), 316 (vh, w) -> consume((int) vh.get(w)), 317 (vh, tv) -> vh.set((int) tv), 318 (vh) -> consume((int) vh.get())); 319 testCaseObjectAccess(cases, "doubleField", double.class, 1234L, 320 (vh, w, tv) -> vh.set(w, (long) tv), 321 (vh, w) -> consume((long) vh.get(w)), 322 (vh, tv) -> vh.set((long) tv), 323 (vh) -> consume((long) vh.get())); 324 testCaseObjectAccess(cases, "doubleField", double.class, Double.valueOf(1234D), 325 (vh, w, tv) -> vh.set(w, (Double) tv), 326 (vh, w) -> consume((Double) vh.get(w)), 327 (vh, tv) -> vh.set((Double) tv), 328 (vh) -> consume((Double) vh.get())); 329 testCaseObjectAccess(cases, "aLongField", Long.class, 1234L, 330 (vh, w, tv) -> vh.set(w, (long) tv), 331 (vh, w) -> consume((long) vh.get(w)), 332 (vh, tv) -> vh.set((long) tv), 333 (vh) -> consume((long) vh.get())); 334 335 return cases.toArray(Object[][]::new); 336 } 337 338 @DataProvider 339 public static Object[][] dataSetArray() { 340 List<Object[]> cases = new ArrayList<>(); 341 342 // create a bunch of different sig-poly call sites 343 testCaseArraySet(cases, Object[].class, "abcd", (vh, arr, tv) -> vh.set((Object[]) arr, 0, (String) tv)); 344 testCaseArraySet(cases, Object[].class, Integer.valueOf(1234), (vh, arr, tv) -> vh.set((Object[]) arr, (Integer) tv)); 345 testCaseArraySet(cases, long[].class, 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (int) tv)); 346 testCaseArraySet(cases, long[].class, (short) 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (short) tv)); 347 testCaseArraySet(cases, long[].class, (char) 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (char) tv)); 348 testCaseArraySet(cases, long[].class, (byte) 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (byte) tv)); 349 testCaseArraySet(cases, long[].class, Long.valueOf(1234L), (vh, arr, tv) -> vh.set((long[]) arr, 0, (Long) tv)); 350 testCaseArraySet(cases, double[].class, 1234F, (vh, arr, tv) -> vh.set((double[]) arr, 0, (float) tv)); 351 testCaseArraySet(cases, double[].class, 1234, (vh, arr, tv) -> vh.set((double[]) arr, 0, (int) tv)); 352 testCaseArraySet(cases, double[].class, 1234L, (vh, arr, tv) -> vh.set((double[]) arr, 0, (long) tv)); 353 testCaseArraySet(cases, double[].class, Double.valueOf(1234D), (vh, arr, tv) -> vh.set((double[]) arr, 0, (Double) tv)); 354 testCaseArraySet(cases, Long[].class, 1234L, (vh, arr, tv) -> vh.set((Long[]) arr, 0, (long) tv)); 355 356 return cases.toArray(Object[][]::new); 357 } 358 359 @DataProvider 360 public static Object[][] dataSetBuffer() { 361 List<Object[]> cases = new ArrayList<>(); 362 363 // create a bunch of different sig-poly call sites 364 testCaseBufferSet(cases, long[].class, 1234, (vh, buff, tv) -> vh.set(buff, 0, (int) tv)); 365 testCaseBufferSet(cases, long[].class, (short) 1234, (vh, buff, tv) -> vh.set(buff, 0, (short) tv)); 366 testCaseBufferSet(cases, long[].class, (char) 1234, (vh, buff, tv) -> vh.set(buff, 0, (char) tv)); 367 testCaseBufferSet(cases, long[].class, (byte) 1234, (vh, buff, tv) -> vh.set(buff, 0, (byte) tv)); 368 testCaseBufferSet(cases, long[].class, Long.valueOf(1234L), (vh, buff, tv) -> vh.set(buff, 0, (Long) tv)); 369 testCaseBufferSet(cases, double[].class, 1234F, (vh, buff, tv) -> vh.set(buff, 0, (float) tv)); 370 testCaseBufferSet(cases, double[].class, 1234, (vh, buff, tv) -> vh.set(buff, 0, (int) tv)); 371 testCaseBufferSet(cases, double[].class, 1234L, (vh, buff, tv) -> vh.set(buff, 0, (long) tv)); 372 testCaseBufferSet(cases, double[].class, Double.valueOf(1234D), (vh, buff, tv) -> vh.set(buff, 0, (Double) tv)); 373 374 return cases.toArray(Object[][]::new); 375 } 376 377 @DataProvider 378 public static Object[][] dataSetMemorySegment() { 379 List<Object[]> cases = new ArrayList<>(); 380 381 // create a bunch of different sig-poly call sites 382 testCaseSegmentSet(cases, long.class, 1234, (vh, seg, off, tv) -> vh.set((MemorySegment) seg, off, (int) tv)); 383 testCaseSegmentSet(cases, long.class, (char) 1234, (vh, seg, off, tv) -> vh.set((MemorySegment) seg, off, (char) tv)); 384 testCaseSegmentSet(cases, long.class, (short) 1234, (vh, seg, off, tv) -> vh.set((MemorySegment) seg, off, (short) tv)); 385 testCaseSegmentSet(cases, long.class, (byte) 1234, (vh, seg, off, tv) -> vh.set((MemorySegment) seg, off, (byte) tv)); 386 testCaseSegmentSet(cases, double.class, 1234F, (vh, seg, off, tv) -> vh.set((MemorySegment) seg, off, (float) tv)); 387 388 return cases.toArray(Object[][]::new); 389 } 390 391 }