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 }