1 /* 2 * Copyright (c) 2015, 2017, 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 import java.lang.invoke.MethodHandle; 25 import java.lang.invoke.MethodHandleInfo; 26 import java.lang.invoke.MethodHandles; 27 import java.lang.invoke.MethodType; 28 import java.lang.invoke.VarHandle; 29 import java.lang.invoke.WrongMethodTypeException; 30 import java.lang.reflect.Method; 31 import java.nio.ReadOnlyBufferException; 32 import java.util.EnumMap; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.stream.Stream; 37 38 import static java.util.stream.Collectors.toList; 39 import static org.testng.Assert.*; 40 41 abstract class VarHandleBaseTest { 42 static final int ITERS = Integer.getInteger("iters", 1); 43 static final int WEAK_ATTEMPTS = Integer.getInteger("weakAttempts", 10); 44 45 interface ThrowingRunnable { 46 void run() throws Throwable; 47 } 48 49 static void checkUOE(ThrowingRunnable r) { 50 checkWithThrowable(UnsupportedOperationException.class, null, r); 51 } 52 53 static void checkUOE(Object message, ThrowingRunnable r) { 54 checkWithThrowable(UnsupportedOperationException.class, message, r); 55 } 56 57 static void checkROBE(ThrowingRunnable r) { 58 checkWithThrowable(ReadOnlyBufferException.class, null, r); 59 } 60 61 static void checkROBE(Object message, ThrowingRunnable r) { 62 checkWithThrowable(ReadOnlyBufferException.class, message, r); 63 } 64 65 static void checkIOOBE(ThrowingRunnable r) { 66 checkWithThrowable(IndexOutOfBoundsException.class, null, r); 67 } 68 69 static void checkIOOBE(Object message, ThrowingRunnable r) { 70 checkWithThrowable(IndexOutOfBoundsException.class, message, r); 71 } 72 73 static void checkASE(ThrowingRunnable r) { 74 checkWithThrowable(ArrayStoreException.class, null, r); 75 } 76 77 static void checkASE(Object message, ThrowingRunnable r) { 78 checkWithThrowable(ArrayStoreException.class, message, r); 79 } 80 81 static void checkISE(ThrowingRunnable r) { 82 checkWithThrowable(IllegalStateException.class, null, r); 83 } 84 85 static void checkISE(Object message, ThrowingRunnable r) { 86 checkWithThrowable(IllegalStateException.class, message, r); 87 } 88 89 static void checkIAE(ThrowingRunnable r) { 90 checkWithThrowable(IllegalAccessException.class, null, r); 91 } 92 93 static void checkIAE(Object message, ThrowingRunnable r) { 94 checkWithThrowable(IllegalAccessException.class, message, r); 95 } 96 97 static void checkWMTE(ThrowingRunnable r) { 98 checkWithThrowable(WrongMethodTypeException.class, null, r); 99 } 100 101 static void checkWMTE(Object message, ThrowingRunnable r) { 102 checkWithThrowable(WrongMethodTypeException.class, message, r); 103 } 104 105 static void checkCCE(ThrowingRunnable r) { 106 checkWithThrowable(ClassCastException.class, null, r); 107 } 108 109 static void checkCCE(Object message, ThrowingRunnable r) { 110 checkWithThrowable(ClassCastException.class, message, r); 111 } 112 113 static void checkNPE(ThrowingRunnable r) { 114 checkWithThrowable(NullPointerException.class, null, r); 115 } 116 117 static void checkNPE(Object message, ThrowingRunnable r) { 118 checkWithThrowable(NullPointerException.class, message, r); 119 } 120 121 static void checkWithThrowable(Class<? extends Throwable> re, 122 Object message, 123 ThrowingRunnable r) { 124 Throwable _e = null; 125 try { 126 r.run(); 127 } 128 catch (Throwable e) { 129 _e = e; 130 } 131 message = message == null ? "" : message + ". "; 132 assertNotNull(_e, String.format("%sNo throwable thrown. Expected %s", message, re)); 133 assertTrue(re.isInstance(_e), String.format("%sIncorrect throwable thrown, %s. Expected %s", message, _e, re)); 134 } 135 136 137 enum TestAccessType { 138 GET, 139 SET, 140 COMPARE_AND_SET, 141 COMPARE_AND_EXCHANGE, 142 GET_AND_SET, 143 GET_AND_ADD, 144 GET_AND_BITWISE; 145 } 146 147 enum TestAccessMode { 148 GET(TestAccessType.GET), 149 SET(TestAccessType.SET), 150 GET_VOLATILE(TestAccessType.GET), 151 SET_VOLATILE(TestAccessType.SET), 152 GET_ACQUIRE(TestAccessType.GET), 153 SET_RELEASE(TestAccessType.SET), 154 GET_OPAQUE(TestAccessType.GET), 155 SET_OPAQUE(TestAccessType.SET), 156 COMPARE_AND_SET(TestAccessType.COMPARE_AND_SET), 157 COMPARE_AND_EXCHANGE(TestAccessType.COMPARE_AND_EXCHANGE), 158 COMPARE_AND_EXCHANGE_ACQUIRE(TestAccessType.COMPARE_AND_EXCHANGE), 159 COMPARE_AND_EXCHANGE_RELEASE(TestAccessType.COMPARE_AND_EXCHANGE), 160 WEAK_COMPARE_AND_SET_PLAIN(TestAccessType.COMPARE_AND_SET), 161 WEAK_COMPARE_AND_SET(TestAccessType.COMPARE_AND_SET), 162 WEAK_COMPARE_AND_SET_ACQUIRE(TestAccessType.COMPARE_AND_SET), 163 WEAK_COMPARE_AND_SET_RELEASE(TestAccessType.COMPARE_AND_SET), 164 GET_AND_SET(TestAccessType.GET_AND_SET), 165 GET_AND_SET_ACQUIRE(TestAccessType.GET_AND_SET), 166 GET_AND_SET_RELEASE(TestAccessType.GET_AND_SET), 167 GET_AND_ADD(TestAccessType.GET_AND_ADD), 168 GET_AND_ADD_ACQUIRE(TestAccessType.GET_AND_ADD), 169 GET_AND_ADD_RELEASE(TestAccessType.GET_AND_ADD), 170 GET_AND_BITWISE_OR(TestAccessType.GET_AND_BITWISE), 171 GET_AND_BITWISE_OR_ACQUIRE(TestAccessType.GET_AND_BITWISE), 172 GET_AND_BITWISE_OR_RELEASE(TestAccessType.GET_AND_BITWISE), 173 GET_AND_BITWISE_AND(TestAccessType.GET_AND_BITWISE), 174 GET_AND_BITWISE_AND_ACQUIRE(TestAccessType.GET_AND_BITWISE), 175 GET_AND_BITWISE_AND_RELEASE(TestAccessType.GET_AND_BITWISE), 176 GET_AND_BITWISE_XOR(TestAccessType.GET_AND_BITWISE), 177 GET_AND_BITWISE_XOR_ACQUIRE(TestAccessType.GET_AND_BITWISE), 178 GET_AND_BITWISE_XOR_RELEASE(TestAccessType.GET_AND_BITWISE), 179 ; 180 181 final TestAccessType at; 182 final boolean isPolyMorphicInReturnType; 183 final Class<?> returnType; 184 185 TestAccessMode(TestAccessType at) { 186 this.at = at; 187 188 try { 189 VarHandle.AccessMode vh_am = toAccessMode(); 190 Method m = VarHandle.class.getMethod(vh_am.methodName(), Object[].class); 191 this.returnType = m.getReturnType(); 192 isPolyMorphicInReturnType = returnType != Object.class; 193 } 194 catch (Exception e) { 195 throw new Error(e); 196 } 197 } 198 199 boolean isOfType(TestAccessType at) { 200 return this.at == at; 201 } 202 203 VarHandle.AccessMode toAccessMode() { 204 return VarHandle.AccessMode.valueOf(name()); 205 } 206 } 207 208 static List<TestAccessMode> testAccessModes() { 209 return Stream.of(TestAccessMode.values()).collect(toList()); 210 } 211 212 static List<TestAccessMode> testAccessModesOfType(TestAccessType... ats) { 213 Stream<TestAccessMode> s = Stream.of(TestAccessMode.values()); 214 return s.filter(e -> Stream.of(ats).anyMatch(e::isOfType)) 215 .collect(toList()); 216 } 217 218 static List<VarHandle.AccessMode> accessModes() { 219 return Stream.of(VarHandle.AccessMode.values()).collect(toList()); 220 } 221 222 static List<VarHandle.AccessMode> accessModesOfType(TestAccessType... ats) { 223 Stream<TestAccessMode> s = Stream.of(TestAccessMode.values()); 224 return s.filter(e -> Stream.of(ats).anyMatch(e::isOfType)) 225 .map(TestAccessMode::toAccessMode) 226 .collect(toList()); 227 } 228 229 static MethodHandle toMethodHandle(VarHandle vh, TestAccessMode tam, MethodType mt) { 230 return vh.toMethodHandle(tam.toAccessMode()); 231 } 232 233 static MethodHandle findVirtual(VarHandle vh, TestAccessMode tam, MethodType mt) { 234 MethodHandle mh; 235 try { 236 mh = MethodHandles.publicLookup(). 237 findVirtual(VarHandle.class, 238 tam.toAccessMode().methodName(), 239 mt); 240 } catch (Exception e) { 241 throw new RuntimeException(e); 242 } 243 return bind(vh, mh, mt); 244 } 245 246 static MethodHandle varHandleInvoker(VarHandle vh, TestAccessMode tam, MethodType mt) { 247 MethodHandle mh = MethodHandles.varHandleInvoker( 248 tam.toAccessMode(), 249 mt); 250 251 return bind(vh, mh, mt); 252 } 253 254 static MethodHandle varHandleExactInvoker(VarHandle vh, TestAccessMode tam, MethodType mt) { 255 MethodHandle mh = MethodHandles.varHandleExactInvoker( 256 tam.toAccessMode(), 257 mt); 258 259 return bind(vh, mh, mt); 260 } 261 262 private static MethodHandle bind(VarHandle vh, MethodHandle mh, MethodType emt) { 263 assertEquals(mh.type(), emt.insertParameterTypes(0, VarHandle.class), 264 "MethodHandle type differs from access mode type"); 265 266 MethodHandleInfo info = MethodHandles.lookup().revealDirect(mh); 267 assertEquals(info.getMethodType(), emt, 268 "MethodHandleInfo method type differs from access mode type"); 269 270 return mh.bindTo(vh); 271 } 272 273 private interface TriFunction<T, U, V, R> { 274 R apply(T t, U u, V v); 275 } 276 277 enum VarHandleToMethodHandle { 278 VAR_HANDLE_TO_METHOD_HANDLE( 279 "VarHandle.toMethodHandle", 280 true, 281 VarHandleBaseTest::toMethodHandle), 282 METHOD_HANDLES_LOOKUP_FIND_VIRTUAL( 283 "Lookup.findVirtual", 284 false, 285 VarHandleBaseTest::findVirtual), 286 METHOD_HANDLES_VAR_HANDLE_INVOKER( 287 "MethodHandles.varHandleInvoker", 288 false, 289 VarHandleBaseTest::varHandleInvoker), 290 METHOD_HANDLES_VAR_HANDLE_EXACT_INVOKER( 291 "MethodHandles.varHandleExactInvoker", 292 true, 293 VarHandleBaseTest::varHandleExactInvoker); 294 295 final String desc; 296 final boolean isExact; 297 final TriFunction<VarHandle, TestAccessMode, MethodType, MethodHandle> f; 298 299 VarHandleToMethodHandle(String desc, boolean isExact, 300 TriFunction<VarHandle, TestAccessMode, MethodType, MethodHandle> f) { 301 this.desc = desc; 302 this.f = f; 303 this.isExact = isExact; 304 } 305 306 MethodHandle apply(VarHandle vh, TestAccessMode am, MethodType mt) { 307 return f.apply(vh, am, mt); 308 } 309 310 @Override 311 public String toString() { 312 return desc; 313 } 314 } 315 316 static class Handles { 317 static class AccessModeAndType { 318 final TestAccessMode tam; 319 final MethodType t; 320 321 public AccessModeAndType(TestAccessMode tam, MethodType t) { 322 this.tam = tam; 323 this.t = t; 324 } 325 326 @Override 327 public boolean equals(Object o) { 328 if (this == o) return true; 329 if (o == null || getClass() != o.getClass()) return false; 330 331 AccessModeAndType x = (AccessModeAndType) o; 332 333 if (tam != x.tam) return false; 334 if (t != null ? !t.equals(x.t) : x.t != null) return false; 335 336 return true; 337 } 338 339 @Override 340 public int hashCode() { 341 int result = tam != null ? tam.hashCode() : 0; 342 result = 31 * result + (t != null ? t.hashCode() : 0); 343 return result; 344 } 345 } 346 347 final VarHandle vh; 348 final VarHandleToMethodHandle f; 349 final EnumMap<TestAccessMode, MethodType> amToType; 350 final Map<AccessModeAndType, MethodHandle> amToHandle; 351 352 Handles(VarHandle vh, VarHandleToMethodHandle f) throws Exception { 353 this.vh = vh; 354 this.f = f; 355 this.amToHandle = new HashMap<>(); 356 357 amToType = new EnumMap<>(TestAccessMode.class); 358 for (TestAccessMode am : testAccessModes()) { 359 amToType.put(am, vh.accessModeType(am.toAccessMode())); 360 } 361 } 362 363 MethodHandle get(TestAccessMode am) { 364 return get(am, amToType.get(am)); 365 } 366 367 MethodHandle get(TestAccessMode am, MethodType mt) { 368 AccessModeAndType amt = new AccessModeAndType(am, mt); 369 return amToHandle.computeIfAbsent( 370 amt, k -> f.apply(vh, am, mt)); 371 } 372 373 Class<? extends Throwable> getWMTEOOrOther(Class<? extends Throwable> c) { 374 return f.isExact ? WrongMethodTypeException.class : c; 375 } 376 377 void checkWMTEOrCCE(ThrowingRunnable r) { 378 checkWithThrowable(getWMTEOOrOther(ClassCastException.class), null, r); 379 } 380 381 } 382 383 interface AccessTestAction<T> { 384 void action(T t) throws Throwable; 385 } 386 387 static abstract class AccessTestCase<T> { 388 final String desc; 389 final AccessTestAction<T> ata; 390 final boolean loop; 391 392 AccessTestCase(String desc, AccessTestAction<T> ata, boolean loop) { 393 this.desc = desc; 394 this.ata = ata; 395 this.loop = loop; 396 } 397 398 boolean requiresLoop() { 399 return loop; 400 } 401 402 abstract T get() throws Exception; 403 404 void testAccess(T t) throws Throwable { 405 ata.action(t); 406 } 407 408 @Override 409 public String toString() { 410 return desc; 411 } 412 } 413 414 static class VarHandleAccessTestCase extends AccessTestCase<VarHandle> { 415 final VarHandle vh; 416 417 VarHandleAccessTestCase(String desc, VarHandle vh, AccessTestAction<VarHandle> ata) { 418 this(desc, vh, ata, true); 419 } 420 421 VarHandleAccessTestCase(String desc, VarHandle vh, AccessTestAction<VarHandle> ata, boolean loop) { 422 super("VarHandle -> " + desc, ata, loop); 423 this.vh = vh; 424 } 425 426 @Override 427 VarHandle get() { 428 return vh; 429 } 430 431 public String toString() { 432 return super.toString() + ", vh:" + vh; 433 } 434 } 435 436 static class MethodHandleAccessTestCase extends AccessTestCase<Handles> { 437 final VarHandle vh; 438 final VarHandleToMethodHandle f; 439 440 MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata) { 441 this(desc, vh, f, ata, true); 442 } 443 444 MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata, boolean loop) { 445 super("VarHandle -> " + f.toString() + " -> " + desc, ata, loop); 446 this.vh = vh; 447 this.f = f; 448 } 449 450 @Override 451 Handles get() throws Exception { 452 return new Handles(vh, f); 453 } 454 455 public String toString() { 456 return super.toString() + ", vh:" + vh + ", f: " + f; 457 } 458 } 459 460 static void testTypes(VarHandle vh) { 461 List<Class<?>> pts = vh.coordinateTypes(); 462 463 for (TestAccessMode accessMode : testAccessModes()) { 464 MethodType amt = vh.accessModeType(accessMode.toAccessMode()); 465 466 assertEquals(amt.parameterList().subList(0, pts.size()), pts); 467 } 468 469 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.GET)) { 470 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode()); 471 assertEquals(mt.returnType(), vh.varType()); 472 assertEquals(mt.parameterList(), pts); 473 } 474 475 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.SET)) { 476 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode()); 477 assertEquals(mt.returnType(), void.class); 478 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType()); 479 } 480 481 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) { 482 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode()); 483 assertEquals(mt.returnType(), boolean.class); 484 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType()); 485 assertEquals(mt.parameterType(mt.parameterCount() - 2), vh.varType()); 486 } 487 488 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) { 489 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode()); 490 assertEquals(mt.returnType(), vh.varType()); 491 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType()); 492 assertEquals(mt.parameterType(mt.parameterCount() - 2), vh.varType()); 493 } 494 495 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.GET_AND_SET, TestAccessType.GET_AND_ADD)) { 496 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode()); 497 assertEquals(mt.returnType(), vh.varType()); 498 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType()); 499 } 500 } 501 }