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 }