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