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 /* 26 * @test 27 * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAdaptVarHandles 28 * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAdaptVarHandles 29 * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAdaptVarHandles 30 * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAdaptVarHandles 31 */ 32 33 import java.lang.foreign.*; 34 35 import org.testng.annotations.*; 36 import static org.testng.Assert.*; 37 38 import java.lang.invoke.MethodHandle; 39 import java.lang.invoke.MethodHandles; 40 import java.lang.invoke.MethodType; 41 import java.lang.invoke.VarHandle; 42 import java.util.List; 43 44 public class TestAdaptVarHandles { 45 46 static MethodHandle S2I; 47 static MethodHandle I2S; 48 static MethodHandle CTX_I2S; 49 static MethodHandle O2I; 50 static MethodHandle I2O; 51 static MethodHandle S2L; 52 static MethodHandle S2L_EX; 53 static MethodHandle S2I_EX; 54 static MethodHandle I2S_EX; 55 static MethodHandle BASE_ADDR; 56 static MethodHandle SUM_OFFSETS; 57 static MethodHandle VOID_FILTER; 58 59 static { 60 try { 61 S2I = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToInt", MethodType.methodType(int.class, String.class)); 62 I2S = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "intToString", MethodType.methodType(String.class, int.class)); 63 CTX_I2S = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "ctxIntToString", 64 MethodType.methodType(String.class, String.class, String.class, int.class)); 65 O2I = MethodHandles.explicitCastArguments(S2I, MethodType.methodType(int.class, Object.class)); 66 I2O = MethodHandles.explicitCastArguments(I2S, MethodType.methodType(Object.class, int.class)); 67 S2L = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLong", MethodType.methodType(long.class, String.class)); 68 S2L_EX = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLongException", MethodType.methodType(long.class, String.class)); 69 BASE_ADDR = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "baseAddress", MethodType.methodType(MemorySegment.class, MemorySegment.class)); 70 SUM_OFFSETS = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "sumOffsets", MethodType.methodType(long.class, long.class, long.class)); 71 VOID_FILTER = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "void_filter", MethodType.methodType(void.class, String.class)); 72 73 MethodHandle s2i_ex = MethodHandles.throwException(int.class, Throwable.class); 74 s2i_ex = MethodHandles.insertArguments(s2i_ex, 0, new Throwable()); 75 S2I_EX = MethodHandles.dropArguments(s2i_ex, 0, String.class); 76 77 MethodHandle i2s_ex = MethodHandles.throwException(String.class, Throwable.class); 78 i2s_ex = MethodHandles.insertArguments(i2s_ex, 0, new Throwable()); 79 I2S_EX = MethodHandles.dropArguments(i2s_ex, 0, int.class); 80 } catch (Throwable ex) { 81 throw new ExceptionInInitializerError(); 82 } 83 } 84 85 static final VarHandle intHandleIndexed = MethodHandles.collectCoordinates(ValueLayout.JAVA_INT.varHandle(), 86 1, MethodHandles.insertArguments(ValueLayout.JAVA_INT.scaleHandle(), 0, 0L)); 87 88 static final VarHandle intHandle = MethodHandles.insertCoordinates(ValueLayout.JAVA_INT.varHandle(), 1, 0L); 89 90 static final VarHandle floatHandle = MethodHandles.insertCoordinates(ValueLayout.JAVA_FLOAT.varHandle(), 1, 0L); 91 92 @Test 93 public void testFilterValue() throws Throwable { 94 ValueLayout layout = ValueLayout.JAVA_INT; 95 Arena scope = Arena.ofAuto(); 96 MemorySegment segment = scope.allocate(layout); 97 VarHandle intHandle = layout.varHandle(); 98 VarHandle i2SHandle = MethodHandles.filterValue(intHandle, S2I, I2S); 99 i2SHandle.set(segment, 0L, "1"); 100 String oldValue = (String)i2SHandle.getAndAdd(segment, 0L, "42"); 101 assertEquals(oldValue, "1"); 102 String value = (String)i2SHandle.get(segment, 0L); 103 assertEquals(value, "43"); 104 boolean swapped = (boolean)i2SHandle.compareAndSet(segment, 0L, "43", "12"); 105 assertTrue(swapped); 106 oldValue = (String)i2SHandle.compareAndExchange(segment, 0L, "12", "42"); 107 assertEquals(oldValue, "12"); 108 value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, 0L); 109 assertEquals(value, "42"); 110 } 111 112 @Test 113 public void testFilterValueComposite() throws Throwable { 114 ValueLayout layout = ValueLayout.JAVA_INT; 115 Arena scope = Arena.ofAuto(); 116 MemorySegment segment = scope.allocate(layout); 117 VarHandle intHandle = layout.varHandle(); 118 MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class); 119 VarHandle i2SHandle = MethodHandles.filterValue(intHandle, CTX_S2I, CTX_I2S); 120 i2SHandle = MethodHandles.insertCoordinates(i2SHandle, 2, "a", "b"); 121 i2SHandle.set(segment, 0L, "1"); 122 String oldValue = (String)i2SHandle.getAndAdd(segment, 0L, "42"); 123 assertEquals(oldValue, "ab1"); 124 String value = (String)i2SHandle.get(segment, 0L); 125 assertEquals(value, "ab43"); 126 boolean swapped = (boolean)i2SHandle.compareAndSet(segment, 0L, "43", "12"); 127 assertTrue(swapped); 128 oldValue = (String)i2SHandle.compareAndExchange(segment, 0L, "12", "42"); 129 assertEquals(oldValue, "ab12"); 130 value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, 0L); 131 assertEquals(value, "ab42"); 132 } 133 134 @Test 135 public void testFilterValueLoose() throws Throwable { 136 ValueLayout layout = ValueLayout.JAVA_INT; 137 Arena scope = Arena.ofAuto(); 138 MemorySegment segment = scope.allocate(layout); 139 VarHandle intHandle = layout.varHandle(); 140 VarHandle i2SHandle = MethodHandles.filterValue(intHandle, O2I, I2O); 141 i2SHandle.set(segment, 0L, "1"); 142 String oldValue = (String)i2SHandle.getAndAdd(segment, 0L, "42"); 143 assertEquals(oldValue, "1"); 144 String value = (String)i2SHandle.get(segment, 0L); 145 assertEquals(value, "43"); 146 boolean swapped = (boolean)i2SHandle.compareAndSet(segment, 0L, "43", "12"); 147 assertTrue(swapped); 148 oldValue = (String)i2SHandle.compareAndExchange(segment, 0L, "12", "42"); 149 assertEquals(oldValue, "12"); 150 value = (String)(Object)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, 0L); 151 assertEquals(value, "42"); 152 } 153 154 @Test(expectedExceptions = IllegalArgumentException.class) 155 public void testBadFilterCarrier() { 156 MethodHandles.filterValue(floatHandle, S2I, I2S); 157 } 158 159 @Test(expectedExceptions = IllegalArgumentException.class) 160 public void testBadFilterUnboxArity() { 161 VarHandle floatHandle = ValueLayout.JAVA_INT.varHandle(); 162 MethodHandles.filterValue(floatHandle, S2I.bindTo(""), I2S); 163 } 164 165 @Test(expectedExceptions = IllegalArgumentException.class) 166 public void testBadFilterBoxArity() { 167 VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); 168 MethodHandles.filterValue(intHandle, S2I, I2S.bindTo(42)); 169 } 170 171 @Test(expectedExceptions = IllegalArgumentException.class) 172 public void testBadFilterBoxPrefixCoordinates() { 173 VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); 174 MethodHandles.filterValue(intHandle, 175 MethodHandles.dropArguments(S2I, 1, int.class), 176 MethodHandles.dropArguments(I2S, 1, long.class)); 177 } 178 179 @Test(expectedExceptions = IllegalArgumentException.class) 180 public void testBadFilterBoxException() { 181 VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); 182 MethodHandles.filterValue(intHandle, I2S, S2L_EX); 183 } 184 185 @Test(expectedExceptions = IllegalArgumentException.class) 186 public void testBadFilterUnboxException() { 187 VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); 188 MethodHandles.filterValue(intHandle, S2L_EX, I2S); 189 } 190 191 @Test(expectedExceptions = IllegalStateException.class) 192 public void testBadFilterBoxHandleException() { 193 VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); 194 VarHandle vh = MethodHandles.filterValue(intHandle, S2I, I2S_EX); 195 try (Arena arena = Arena.ofConfined()) { 196 MemorySegment seg = arena.allocate(ValueLayout.JAVA_INT); 197 vh.set(seg, 0L, "42"); 198 String x = (String) vh.get(seg, 0L); // should throw 199 } 200 } 201 202 @Test(expectedExceptions = IllegalStateException.class) 203 public void testBadFilterUnboxHandleException() { 204 VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); 205 VarHandle vh = MethodHandles.filterValue(intHandle, S2I_EX, I2S); 206 try (Arena arena = Arena.ofConfined()) { 207 MemorySegment seg = arena.allocate(ValueLayout.JAVA_INT); 208 vh.set(seg, 0L, "42"); // should throw 209 } 210 } 211 212 @Test 213 public void testFilterCoordinates() throws Throwable { 214 ValueLayout layout = ValueLayout.JAVA_INT; 215 Arena scope = Arena.ofAuto(); 216 MemorySegment segment = scope.allocate(layout); 217 VarHandle intHandle_longIndex = MethodHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L); 218 intHandle_longIndex.set(segment, "0", 1); 219 int oldValue = (int)intHandle_longIndex.getAndAdd(segment, "0", 42); 220 assertEquals(oldValue, 1); 221 int value = (int)intHandle_longIndex.get(segment, "0"); 222 assertEquals(value, 43); 223 boolean swapped = (boolean)intHandle_longIndex.compareAndSet(segment, "0", 43, 12); 224 assertTrue(swapped); 225 oldValue = (int)intHandle_longIndex.compareAndExchange(segment, "0", 12, 42); 226 assertEquals(oldValue, 12); 227 value = (int)intHandle_longIndex.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, "0"); 228 assertEquals(value, 42); 229 } 230 231 @Test(expectedExceptions = IllegalArgumentException.class) 232 public void testBadFilterCoordinatesNegativePos() { 233 MethodHandles.filterCoordinates(intHandle, -1, SUM_OFFSETS); 234 } 235 236 @Test(expectedExceptions = IllegalArgumentException.class) 237 public void testBadFilterCoordinatesPosTooBig() { 238 MethodHandles.filterCoordinates(intHandle, 1, SUM_OFFSETS); 239 } 240 241 @Test(expectedExceptions = IllegalArgumentException.class) 242 public void testBadFilterCoordinatesWrongFilterType() { 243 MethodHandles.filterCoordinates(intHandleIndexed, 1, S2I); 244 } 245 246 @Test(expectedExceptions = IllegalArgumentException.class) 247 public void testBadFilterCoordinatesWrongFilterException() { 248 MethodHandles.filterCoordinates(intHandleIndexed, 1, S2L_EX); 249 } 250 251 @Test(expectedExceptions = IllegalArgumentException.class) 252 public void testBadFilterCoordinatesTooManyFilters() { 253 MethodHandles.filterCoordinates(intHandleIndexed, 1, S2L, S2L); 254 } 255 256 @Test 257 public void testInsertCoordinates() throws Throwable { 258 ValueLayout layout = ValueLayout.JAVA_INT; 259 Arena scope = Arena.ofAuto(); 260 MemorySegment segment = scope.allocate(layout); 261 VarHandle intHandle_longIndex = MethodHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L); 262 intHandle_longIndex.set(1); 263 int oldValue = (int)intHandle_longIndex.getAndAdd(42); 264 assertEquals(oldValue, 1); 265 int value = (int)intHandle_longIndex.get(); 266 assertEquals(value, 43); 267 boolean swapped = (boolean)intHandle_longIndex.compareAndSet(43, 12); 268 assertTrue(swapped); 269 oldValue = (int)intHandle_longIndex.compareAndExchange(12, 42); 270 assertEquals(oldValue, 12); 271 value = (int)intHandle_longIndex.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(); 272 assertEquals(value, 42); 273 } 274 275 @Test(expectedExceptions = IllegalArgumentException.class) 276 public void testBadInsertCoordinatesNegativePos() { 277 MethodHandles.insertCoordinates(intHandle, -1, 42); 278 } 279 280 @Test(expectedExceptions = IllegalArgumentException.class) 281 public void testBadInsertCoordinatesPosTooBig() { 282 MethodHandles.insertCoordinates(intHandle, 1, 42); 283 } 284 285 @Test(expectedExceptions = ClassCastException.class) 286 public void testBadInsertCoordinatesWrongCoordinateType() { 287 MethodHandles.insertCoordinates(intHandleIndexed, 1, "Hello"); 288 } 289 290 @Test(expectedExceptions = IllegalArgumentException.class) 291 public void testBadInsertCoordinatesTooManyValues() { 292 MethodHandles.insertCoordinates(intHandleIndexed, 1, 0L, 0L); 293 } 294 295 @Test 296 public void testPermuteCoordinates() throws Throwable { 297 ValueLayout layout = ValueLayout.JAVA_INT; 298 Arena scope = Arena.ofAuto(); 299 MemorySegment segment = scope.allocate(layout); 300 VarHandle intHandle_swap = MethodHandles.permuteCoordinates(intHandleIndexed, 301 List.of(long.class, MemorySegment.class), 1, 0); 302 intHandle_swap.set(0L, segment, 1); 303 int oldValue = (int)intHandle_swap.getAndAdd(0L, segment, 42); 304 assertEquals(oldValue, 1); 305 int value = (int)intHandle_swap.get(0L, segment); 306 assertEquals(value, 43); 307 boolean swapped = (boolean)intHandle_swap.compareAndSet(0L, segment, 43, 12); 308 assertTrue(swapped); 309 oldValue = (int)intHandle_swap.compareAndExchange(0L, segment, 12, 42); 310 assertEquals(oldValue, 12); 311 value = (int)intHandle_swap.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(0L, segment); 312 assertEquals(value, 42); 313 } 314 315 @Test(expectedExceptions = IllegalArgumentException.class) 316 public void testBadPermuteCoordinatesTooManyCoordinates() { 317 MethodHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), new int[2]); 318 } 319 320 @Test(expectedExceptions = IllegalArgumentException.class) 321 public void testBadPermuteCoordinatesTooFewCoordinates() { 322 MethodHandles.permuteCoordinates(intHandle, List.of()); 323 } 324 325 @Test(expectedExceptions = IllegalArgumentException.class) 326 public void testBadPermuteCoordinatesIndexTooBig() { 327 MethodHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), 3); 328 } 329 330 @Test(expectedExceptions = IllegalArgumentException.class) 331 public void testBadPermuteCoordinatesIndexTooSmall() { 332 MethodHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), -1); 333 } 334 335 @Test 336 public void testCollectCoordinates() throws Throwable { 337 ValueLayout layout = ValueLayout.JAVA_INT; 338 Arena scope = Arena.ofAuto(); 339 MemorySegment segment = scope.allocate(layout); 340 VarHandle intHandle_sum = MethodHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS); 341 intHandle_sum.set(segment, -2L, 2L, 1); 342 int oldValue = (int)intHandle_sum.getAndAdd(segment, -2L, 2L, 42); 343 assertEquals(oldValue, 1); 344 int value = (int)intHandle_sum.get(segment, -2L, 2L); 345 assertEquals(value, 43); 346 boolean swapped = (boolean)intHandle_sum.compareAndSet(segment, -2L, 2L, 43, 12); 347 assertTrue(swapped); 348 oldValue = (int)intHandle_sum.compareAndExchange(segment, -2L, 2L, 12, 42); 349 assertEquals(oldValue, 12); 350 value = (int)intHandle_sum.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, -2L, 2L); 351 assertEquals(value, 42); 352 } 353 354 @Test 355 public void testCollectCoordinatesVoidFilterType() { 356 VarHandle handle = MethodHandles.collectCoordinates(intHandle, 0, VOID_FILTER); 357 assertEquals(handle.coordinateTypes(), List.of(String.class, MemorySegment.class)); 358 } 359 360 @Test(expectedExceptions = IllegalArgumentException.class) 361 public void testBadCollectCoordinatesNegativePos() { 362 MethodHandles.collectCoordinates(intHandle, -1, SUM_OFFSETS); 363 } 364 365 @Test(expectedExceptions = IllegalArgumentException.class) 366 public void testBadCollectCoordinatesPosTooBig() { 367 MethodHandles.collectCoordinates(intHandle, 1, SUM_OFFSETS); 368 } 369 370 @Test(expectedExceptions = IllegalArgumentException.class) 371 public void testBadCollectCoordinatesWrongFilterType() { 372 MethodHandles.collectCoordinates(intHandle, 0, SUM_OFFSETS); 373 } 374 375 @Test(expectedExceptions = IllegalArgumentException.class) 376 public void testBadCollectCoordinatesWrongFilterException() { 377 MethodHandles.collectCoordinates(intHandle, 0, S2L_EX); 378 } 379 380 @Test 381 public void testDropCoordinates() throws Throwable { 382 ValueLayout layout = ValueLayout.JAVA_INT; 383 Arena scope = Arena.ofAuto(); 384 MemorySegment segment = scope.allocate(layout); 385 VarHandle intHandle_dummy = MethodHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class); 386 intHandle_dummy.set(segment, 1f, "hello", 0L, 1); 387 int oldValue = (int)intHandle_dummy.getAndAdd(segment, 1f, "hello", 0L, 42); 388 assertEquals(oldValue, 1); 389 int value = (int)intHandle_dummy.get(segment, 1f, "hello", 0L); 390 assertEquals(value, 43); 391 boolean swapped = (boolean)intHandle_dummy.compareAndSet(segment, 1f, "hello", 0L, 43, 12); 392 assertTrue(swapped); 393 oldValue = (int)intHandle_dummy.compareAndExchange(segment, 1f, "hello", 0L, 12, 42); 394 assertEquals(oldValue, 12); 395 value = (int)intHandle_dummy.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, 1f, "hello", 0L); 396 assertEquals(value, 42); 397 } 398 399 @Test(expectedExceptions = IllegalArgumentException.class) 400 public void testBadDropCoordinatesNegativePos() { 401 MethodHandles.dropCoordinates(intHandle, -1); 402 } 403 404 @Test(expectedExceptions = IllegalArgumentException.class) 405 public void testBadDropCoordinatesPosTooBig() { 406 MethodHandles.dropCoordinates(intHandle, 2); 407 } 408 409 //helper methods 410 411 static int stringToInt(String s) { 412 return Integer.valueOf(s); 413 } 414 415 static String intToString(int i) { 416 return String.valueOf(i); 417 } 418 419 static long stringToLong(String s) { 420 return Long.valueOf(s); 421 } 422 423 static long stringToLongException(String s) throws Throwable { 424 return Long.valueOf(s); 425 } 426 427 static MemorySegment baseAddress(MemorySegment segment) { 428 return segment; 429 } 430 431 static long sumOffsets(long l1, long l2) { 432 return l1 + l2; 433 } 434 435 static void void_filter(String s) { } 436 437 static String ctxIntToString(String a, String b, int i) { 438 return a + b + String.valueOf(i); 439 } 440 }