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