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 {
run()46         void run() throws Throwable;
47     }
48 
checkUOE(ThrowingRunnable r)49     static void checkUOE(ThrowingRunnable r) {
50         checkWithThrowable(UnsupportedOperationException.class, null, r);
51     }
52 
checkUOE(Object message, ThrowingRunnable r)53     static void checkUOE(Object message, ThrowingRunnable r) {
54         checkWithThrowable(UnsupportedOperationException.class, message, r);
55     }
56 
checkROBE(ThrowingRunnable r)57     static void checkROBE(ThrowingRunnable r) {
58         checkWithThrowable(ReadOnlyBufferException.class, null, r);
59     }
60 
checkROBE(Object message, ThrowingRunnable r)61     static void checkROBE(Object message, ThrowingRunnable r) {
62         checkWithThrowable(ReadOnlyBufferException.class, message, r);
63     }
64 
checkIOOBE(ThrowingRunnable r)65     static void checkIOOBE(ThrowingRunnable r) {
66         checkWithThrowable(IndexOutOfBoundsException.class, null, r);
67     }
68 
checkIOOBE(Object message, ThrowingRunnable r)69     static void checkIOOBE(Object message, ThrowingRunnable r) {
70         checkWithThrowable(IndexOutOfBoundsException.class, message, r);
71     }
72 
checkASE(ThrowingRunnable r)73     static void checkASE(ThrowingRunnable r) {
74         checkWithThrowable(ArrayStoreException.class, null, r);
75     }
76 
checkASE(Object message, ThrowingRunnable r)77     static void checkASE(Object message, ThrowingRunnable r) {
78         checkWithThrowable(ArrayStoreException.class, message, r);
79     }
80 
checkISE(ThrowingRunnable r)81     static void checkISE(ThrowingRunnable r) {
82         checkWithThrowable(IllegalStateException.class, null, r);
83     }
84 
checkISE(Object message, ThrowingRunnable r)85     static void checkISE(Object message, ThrowingRunnable r) {
86         checkWithThrowable(IllegalStateException.class, message, r);
87     }
88 
checkIAE(ThrowingRunnable r)89     static void checkIAE(ThrowingRunnable r) {
90         checkWithThrowable(IllegalAccessException.class, null, r);
91     }
92 
checkIAE(Object message, ThrowingRunnable r)93     static void checkIAE(Object message, ThrowingRunnable r) {
94         checkWithThrowable(IllegalAccessException.class, message, r);
95     }
96 
checkWMTE(ThrowingRunnable r)97     static void checkWMTE(ThrowingRunnable r) {
98         checkWithThrowable(WrongMethodTypeException.class, null, r);
99     }
100 
checkWMTE(Object message, ThrowingRunnable r)101     static void checkWMTE(Object message, ThrowingRunnable r) {
102         checkWithThrowable(WrongMethodTypeException.class, message, r);
103     }
104 
checkCCE(ThrowingRunnable r)105     static void checkCCE(ThrowingRunnable r) {
106         checkWithThrowable(ClassCastException.class, null, r);
107     }
108 
checkCCE(Object message, ThrowingRunnable r)109     static void checkCCE(Object message, ThrowingRunnable r) {
110         checkWithThrowable(ClassCastException.class, message, r);
111     }
112 
checkNPE(ThrowingRunnable r)113     static void checkNPE(ThrowingRunnable r) {
114         checkWithThrowable(NullPointerException.class, null, r);
115     }
116 
checkNPE(Object message, ThrowingRunnable r)117     static void checkNPE(Object message, ThrowingRunnable r) {
118         checkWithThrowable(NullPointerException.class, message, r);
119     }
120 
checkWithThrowable(Class<? extends Throwable> re, Object message, ThrowingRunnable r)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 
TestAccessMode(TestAccessType at)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 
isOfType(TestAccessType at)199         boolean isOfType(TestAccessType at) {
200             return this.at == at;
201         }
202 
toAccessMode()203         VarHandle.AccessMode toAccessMode() {
204             return VarHandle.AccessMode.valueOf(name());
205         }
206     }
207 
testAccessModes()208     static List<TestAccessMode> testAccessModes() {
209         return Stream.of(TestAccessMode.values()).collect(toList());
210     }
211 
testAccessModesOfType(TestAccessType... ats)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 
accessModes()218     static List<VarHandle.AccessMode> accessModes() {
219         return Stream.of(VarHandle.AccessMode.values()).collect(toList());
220     }
221 
accessModesOfType(TestAccessType... ats)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 
toMethodHandle(VarHandle vh, TestAccessMode tam, MethodType mt)229     static MethodHandle toMethodHandle(VarHandle vh, TestAccessMode tam, MethodType mt) {
230         return vh.toMethodHandle(tam.toAccessMode());
231     }
232 
findVirtual(VarHandle vh, TestAccessMode tam, MethodType mt)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 
varHandleInvoker(VarHandle vh, TestAccessMode tam, MethodType mt)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 
varHandleExactInvoker(VarHandle vh, TestAccessMode tam, MethodType mt)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 
bind(VarHandle vh, MethodHandle mh, MethodType emt)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> {
apply(T t, U u, V v)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 
VarHandleToMethodHandle(String desc, boolean isExact, TriFunction<VarHandle, TestAccessMode, MethodType, MethodHandle> f)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 
apply(VarHandle vh, TestAccessMode am, MethodType mt)306         MethodHandle apply(VarHandle vh, TestAccessMode am, MethodType mt) {
307             return f.apply(vh, am, mt);
308         }
309 
310         @Override
toString()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 
AccessModeAndType(TestAccessMode tam, MethodType t)321             public AccessModeAndType(TestAccessMode tam, MethodType t) {
322                 this.tam = tam;
323                 this.t = t;
324             }
325 
326             @Override
equals(Object o)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
hashCode()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 
Handles(VarHandle vh, VarHandleToMethodHandle f)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 
get(TestAccessMode am)363         MethodHandle get(TestAccessMode am) {
364             return get(am, amToType.get(am));
365         }
366 
get(TestAccessMode am, MethodType mt)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 
getWMTEOOrOther(Class<? extends Throwable> c)373         Class<? extends Throwable> getWMTEOOrOther(Class<? extends Throwable> c) {
374             return f.isExact ? WrongMethodTypeException.class : c;
375         }
376 
checkWMTEOrCCE(ThrowingRunnable r)377         void checkWMTEOrCCE(ThrowingRunnable r) {
378             checkWithThrowable(getWMTEOOrOther(ClassCastException.class), null, r);
379         }
380 
381     }
382 
383     interface AccessTestAction<T> {
action(T 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 
AccessTestCase(String desc, AccessTestAction<T> ata, boolean loop)392         AccessTestCase(String desc, AccessTestAction<T> ata, boolean loop) {
393             this.desc = desc;
394             this.ata = ata;
395             this.loop = loop;
396         }
397 
requiresLoop()398         boolean requiresLoop() {
399             return loop;
400         }
401 
get()402         abstract T get() throws Exception;
403 
testAccess(T t)404         void testAccess(T t) throws Throwable {
405             ata.action(t);
406         }
407 
408         @Override
toString()409         public String toString() {
410             return desc;
411         }
412     }
413 
414     static class VarHandleAccessTestCase extends AccessTestCase<VarHandle> {
415         final VarHandle vh;
416 
VarHandleAccessTestCase(String desc, VarHandle vh, AccessTestAction<VarHandle> ata)417         VarHandleAccessTestCase(String desc, VarHandle vh, AccessTestAction<VarHandle> ata) {
418             this(desc, vh, ata, true);
419         }
420 
VarHandleAccessTestCase(String desc, VarHandle vh, AccessTestAction<VarHandle> ata, boolean loop)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
get()427         VarHandle get() {
428             return vh;
429         }
430     }
431 
432     static class MethodHandleAccessTestCase extends AccessTestCase<Handles> {
433         final VarHandle vh;
434         final VarHandleToMethodHandle f;
435 
MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata)436         MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata) {
437             this(desc, vh, f, ata, true);
438         }
439 
MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata, boolean loop)440         MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata, boolean loop) {
441             super("VarHandle -> " + f.toString() + " -> " + desc, ata, loop);
442             this.vh = vh;
443             this.f = f;
444         }
445 
446         @Override
get()447         Handles get() throws Exception {
448             return new Handles(vh, f);
449         }
450     }
451 
testTypes(VarHandle vh)452     static void testTypes(VarHandle vh) {
453         List<Class<?>> pts = vh.coordinateTypes();
454 
455         for (TestAccessMode accessMode : testAccessModes()) {
456             MethodType amt = vh.accessModeType(accessMode.toAccessMode());
457 
458             assertEquals(amt.parameterList().subList(0, pts.size()), pts);
459         }
460 
461         for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.GET)) {
462             MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
463             assertEquals(mt.returnType(), vh.varType());
464             assertEquals(mt.parameterList(), pts);
465         }
466 
467         for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.SET)) {
468             MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
469             assertEquals(mt.returnType(), void.class);
470             assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
471         }
472 
473         for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) {
474             MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
475             assertEquals(mt.returnType(), boolean.class);
476             assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
477             assertEquals(mt.parameterType(mt.parameterCount() - 2), vh.varType());
478         }
479 
480         for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) {
481             MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
482             assertEquals(mt.returnType(), vh.varType());
483             assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
484             assertEquals(mt.parameterType(mt.parameterCount() - 2), vh.varType());
485         }
486 
487         for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.GET_AND_SET, TestAccessType.GET_AND_ADD)) {
488             MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
489             assertEquals(mt.returnType(), vh.varType());
490             assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
491         }
492     }
493 }
494