1 /*
2 * Copyright (c) 2015, 2023, 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
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 Throwable _e = null;
140 try {
141 r.run();
142 }
143 catch (Throwable e) {
144 _e = e;
145 }
146 message = message == null ? "" : message + ". ";
147 assertNotNull(_e, String.format("%sNo throwable thrown. Expected %s", message, re));
148 if (!re.isInstance(_e)) {
149 fail(String.format("%sIncorrect throwable thrown, %s. Expected %s", message, _e, re), _e);
150 }
151 }
152
153
154 enum TestAccessType {
155 GET,
156 SET,
157 COMPARE_AND_SET,
158 COMPARE_AND_EXCHANGE,
159 GET_AND_SET,
160 GET_AND_ADD,
161 GET_AND_BITWISE;
162 }
163
164 enum TestAccessMode {
165 GET(TestAccessType.GET),
166 SET(TestAccessType.SET),
167 GET_VOLATILE(TestAccessType.GET),
168 SET_VOLATILE(TestAccessType.SET),
169 GET_ACQUIRE(TestAccessType.GET),
170 SET_RELEASE(TestAccessType.SET),
171 GET_OPAQUE(TestAccessType.GET),
172 SET_OPAQUE(TestAccessType.SET),
173 COMPARE_AND_SET(TestAccessType.COMPARE_AND_SET),
174 COMPARE_AND_EXCHANGE(TestAccessType.COMPARE_AND_EXCHANGE),
175 COMPARE_AND_EXCHANGE_ACQUIRE(TestAccessType.COMPARE_AND_EXCHANGE),
176 COMPARE_AND_EXCHANGE_RELEASE(TestAccessType.COMPARE_AND_EXCHANGE),
177 WEAK_COMPARE_AND_SET_PLAIN(TestAccessType.COMPARE_AND_SET),
178 WEAK_COMPARE_AND_SET(TestAccessType.COMPARE_AND_SET),
179 WEAK_COMPARE_AND_SET_ACQUIRE(TestAccessType.COMPARE_AND_SET),
180 WEAK_COMPARE_AND_SET_RELEASE(TestAccessType.COMPARE_AND_SET),
181 GET_AND_SET(TestAccessType.GET_AND_SET),
182 GET_AND_SET_ACQUIRE(TestAccessType.GET_AND_SET),
183 GET_AND_SET_RELEASE(TestAccessType.GET_AND_SET),
184 GET_AND_ADD(TestAccessType.GET_AND_ADD),
185 GET_AND_ADD_ACQUIRE(TestAccessType.GET_AND_ADD),
186 GET_AND_ADD_RELEASE(TestAccessType.GET_AND_ADD),
187 GET_AND_BITWISE_OR(TestAccessType.GET_AND_BITWISE),
188 GET_AND_BITWISE_OR_ACQUIRE(TestAccessType.GET_AND_BITWISE),
189 GET_AND_BITWISE_OR_RELEASE(TestAccessType.GET_AND_BITWISE),
190 GET_AND_BITWISE_AND(TestAccessType.GET_AND_BITWISE),
191 GET_AND_BITWISE_AND_ACQUIRE(TestAccessType.GET_AND_BITWISE),
192 GET_AND_BITWISE_AND_RELEASE(TestAccessType.GET_AND_BITWISE),
193 GET_AND_BITWISE_XOR(TestAccessType.GET_AND_BITWISE),
194 GET_AND_BITWISE_XOR_ACQUIRE(TestAccessType.GET_AND_BITWISE),
195 GET_AND_BITWISE_XOR_RELEASE(TestAccessType.GET_AND_BITWISE),
196 ;
197
198 final TestAccessType at;
199 final boolean isPolyMorphicInReturnType;
200 final Class<?> returnType;
201
202 TestAccessMode(TestAccessType at) {
203 this.at = at;
204
205 try {
206 VarHandle.AccessMode vh_am = toAccessMode();
207 Method m = VarHandle.class.getMethod(vh_am.methodName(), Object[].class);
208 this.returnType = m.getReturnType();
209 isPolyMorphicInReturnType = returnType != Object.class;
210 }
211 catch (Exception e) {
212 throw new Error(e);
213 }
214 }
215
216 boolean isOfType(TestAccessType at) {
217 return this.at == at;
218 }
219
220 VarHandle.AccessMode toAccessMode() {
221 return VarHandle.AccessMode.valueOf(name());
222 }
223 }
224
225 static List<TestAccessMode> testAccessModes() {
226 return Stream.of(TestAccessMode.values()).collect(toList());
227 }
228
229 static List<TestAccessMode> testAccessModesOfType(TestAccessType... ats) {
230 Stream<TestAccessMode> s = Stream.of(TestAccessMode.values());
231 return s.filter(e -> Stream.of(ats).anyMatch(e::isOfType))
232 .collect(toList());
233 }
234
235 static List<VarHandle.AccessMode> accessModes() {
236 return Stream.of(VarHandle.AccessMode.values()).collect(toList());
237 }
238
239 static List<VarHandle.AccessMode> accessModesOfType(TestAccessType... ats) {
240 Stream<TestAccessMode> s = Stream.of(TestAccessMode.values());
241 return s.filter(e -> Stream.of(ats).anyMatch(e::isOfType))
242 .map(TestAccessMode::toAccessMode)
243 .collect(toList());
244 }
245
246 static MethodHandle toMethodHandle(VarHandle vh, TestAccessMode tam, MethodType mt) {
247 return vh.toMethodHandle(tam.toAccessMode());
248 }
249
250 static MethodHandle findVirtual(VarHandle vh, TestAccessMode tam, MethodType mt) {
251 MethodHandle mh;
252 try {
253 mh = MethodHandles.publicLookup().
254 findVirtual(VarHandle.class,
255 tam.toAccessMode().methodName(),
256 mt);
257 } catch (Exception e) {
258 throw new RuntimeException(e);
259 }
260 return bind(vh, mh, mt);
261 }
262
263 static MethodHandle varHandleInvoker(VarHandle vh, TestAccessMode tam, MethodType mt) {
264 MethodHandle mh = MethodHandles.varHandleInvoker(
265 tam.toAccessMode(),
266 mt);
267
268 return bind(vh, mh, mt);
269 }
270
271 static MethodHandle varHandleExactInvoker(VarHandle vh, TestAccessMode tam, MethodType mt) {
272 MethodHandle mh = MethodHandles.varHandleExactInvoker(
273 tam.toAccessMode(),
274 mt);
275
276 return bind(vh, mh, mt);
277 }
278
279 private static MethodHandle bind(VarHandle vh, MethodHandle mh, MethodType emt) {
280 assertEquals(mh.type(), emt.insertParameterTypes(0, VarHandle.class),
281 "MethodHandle type differs from access mode type");
282
283 MethodHandleInfo info = MethodHandles.lookup().revealDirect(mh);
284 assertEquals(info.getMethodType(), emt,
285 "MethodHandleInfo method type differs from access mode type");
286
287 return mh.bindTo(vh);
288 }
289
290 private interface TriFunction<T, U, V, R> {
291 R apply(T t, U u, V v);
292 }
293
294 enum VarHandleToMethodHandle {
295 VAR_HANDLE_TO_METHOD_HANDLE(
296 "VarHandle.toMethodHandle",
297 true,
298 VarHandleBaseTest::toMethodHandle),
299 METHOD_HANDLES_LOOKUP_FIND_VIRTUAL(
300 "Lookup.findVirtual",
301 false,
302 VarHandleBaseTest::findVirtual),
303 METHOD_HANDLES_VAR_HANDLE_INVOKER(
304 "MethodHandles.varHandleInvoker",
305 false,
306 VarHandleBaseTest::varHandleInvoker),
307 METHOD_HANDLES_VAR_HANDLE_EXACT_INVOKER(
308 "MethodHandles.varHandleExactInvoker",
309 true,
310 VarHandleBaseTest::varHandleExactInvoker);
311
312 final String desc;
313 final boolean isExact;
314 final TriFunction<VarHandle, TestAccessMode, MethodType, MethodHandle> f;
315
316 VarHandleToMethodHandle(String desc, boolean isExact,
317 TriFunction<VarHandle, TestAccessMode, MethodType, MethodHandle> f) {
318 this.desc = desc;
319 this.f = f;
320 this.isExact = isExact;
321 }
322
323 MethodHandle apply(VarHandle vh, TestAccessMode am, MethodType mt) {
324 return f.apply(vh, am, mt);
325 }
326
327 @Override
328 public String toString() {
329 return desc;
330 }
331 }
332
333 static class Handles {
334 static class AccessModeAndType {
335 final TestAccessMode tam;
336 final MethodType t;
337
338 public AccessModeAndType(TestAccessMode tam, MethodType t) {
339 this.tam = tam;
340 this.t = t;
341 }
342
343 @Override
344 public boolean equals(Object o) {
345 if (this == o) return true;
346 if (o == null || getClass() != o.getClass()) return false;
347
348 AccessModeAndType x = (AccessModeAndType) o;
349
350 if (tam != x.tam) return false;
351 if (t != null ? !t.equals(x.t) : x.t != null) return false;
352
353 return true;
354 }
355
356 @Override
357 public int hashCode() {
358 int result = tam != null ? tam.hashCode() : 0;
359 result = 31 * result + (t != null ? t.hashCode() : 0);
360 return result;
361 }
362 }
363
364 final VarHandle vh;
365 final VarHandleToMethodHandle f;
366 final EnumMap<TestAccessMode, MethodType> amToType;
367 final Map<AccessModeAndType, MethodHandle> amToHandle;
368
369 Handles(VarHandle vh, VarHandleToMethodHandle f) throws Exception {
370 this.vh = vh;
371 this.f = f;
372 this.amToHandle = new HashMap<>();
373
374 amToType = new EnumMap<>(TestAccessMode.class);
375 for (TestAccessMode am : testAccessModes()) {
376 amToType.put(am, vh.accessModeType(am.toAccessMode()));
377 }
378 }
379
380 MethodHandle get(TestAccessMode am) {
381 return get(am, amToType.get(am));
382 }
383
384 MethodHandle get(TestAccessMode am, MethodType mt) {
385 AccessModeAndType amt = new AccessModeAndType(am, mt);
386 return amToHandle.computeIfAbsent(
387 amt, k -> f.apply(vh, am, mt));
388 }
389
390 Class<? extends Throwable> getWMTEOOrOther(Class<? extends Throwable> c) {
391 return f.isExact ? WrongMethodTypeException.class : c;
392 }
393
394 void checkWMTEOrCCE(ThrowingRunnable r) {
395 checkWithThrowable(getWMTEOOrOther(ClassCastException.class), null, r);
396 }
397
398 }
399
400 interface AccessTestAction<T> {
401 void action(T t) throws Throwable;
402 }
403
404 static abstract class AccessTestCase<T> {
405 final String desc;
406 final AccessTestAction<T> ata;
407 final boolean loop;
408
409 AccessTestCase(String desc, AccessTestAction<T> ata, boolean loop) {
410 this.desc = desc;
411 this.ata = ata;
412 this.loop = loop;
413 }
414
415 boolean requiresLoop() {
416 return loop;
417 }
418
419 abstract T get() throws Exception;
420
421 void testAccess(T t) throws Throwable {
422 ata.action(t);
423 }
424
425 @Override
426 public String toString() {
427 return desc;
428 }
429 }
430
431 static class VarHandleAccessTestCase extends AccessTestCase<VarHandle> {
432 final VarHandle vh;
433
434 VarHandleAccessTestCase(String desc, VarHandle vh, AccessTestAction<VarHandle> ata) {
435 this(desc, vh, ata, true);
436 }
437
438 VarHandleAccessTestCase(String desc, VarHandle vh, AccessTestAction<VarHandle> ata, boolean loop) {
439 super("VarHandle -> " + desc, ata, loop);
440 this.vh = vh;
441 }
442
443 @Override
444 VarHandle get() {
445 return vh;
446 }
447
448 public String toString() {
449 return super.toString() + ", vh:" + vh;
450 }
451 }
452
453 static class MethodHandleAccessTestCase extends AccessTestCase<Handles> {
454 final VarHandle vh;
455 final VarHandleToMethodHandle f;
456
457 MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata) {
458 this(desc, vh, f, ata, true);
459 }
460
461 MethodHandleAccessTestCase(String desc, VarHandle vh, VarHandleToMethodHandle f, AccessTestAction<Handles> ata, boolean loop) {
462 super("VarHandle -> " + f.toString() + " -> " + desc, ata, loop);
463 this.vh = vh;
464 this.f = f;
465 }
466
467 @Override
468 Handles get() throws Exception {
469 return new Handles(vh, f);
470 }
471
472 public String toString() {
473 return super.toString() + ", vh:" + vh + ", f: " + f;
474 }
475 }
476
477 static void testTypes(VarHandle vh) {
478 List<Class<?>> pts = vh.coordinateTypes();
479
480 for (TestAccessMode accessMode : testAccessModes()) {
481 MethodType amt = vh.accessModeType(accessMode.toAccessMode());
482
483 assertEquals(amt.parameterList().subList(0, pts.size()), pts);
484 }
485
486 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.GET)) {
487 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
488 assertEquals(mt.returnType(), vh.varType());
489 assertEquals(mt.parameterList(), pts);
490 }
491
492 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.SET)) {
493 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
494 assertEquals(mt.returnType(), void.class);
495 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
496 }
497
498 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) {
499 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
500 assertEquals(mt.returnType(), boolean.class);
501 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
502 assertEquals(mt.parameterType(mt.parameterCount() - 2), vh.varType());
503 }
504
505 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) {
506 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
507 assertEquals(mt.returnType(), vh.varType());
508 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
509 assertEquals(mt.parameterType(mt.parameterCount() - 2), vh.varType());
510 }
511
512 for (TestAccessMode testAccessMode : testAccessModesOfType(TestAccessType.GET_AND_SET, TestAccessType.GET_AND_ADD)) {
513 MethodType mt = vh.accessModeType(testAccessMode.toAccessMode());
514 assertEquals(mt.returnType(), vh.varType());
515 assertEquals(mt.parameterType(mt.parameterCount() - 1), vh.varType());
516 }
517 }
518
519 static void weakDelay() {
520 try {
521 if (WEAK_DELAY_MS > 0) {
522 Thread.sleep(WEAK_DELAY_MS);
523 }
524 } catch (InterruptedException ie) {
525 // Do nothing.
526 }
527 }
528 }
--- EOF ---