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