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