|
23 | 23 |
|
24 | 24 | /* |
25 | 25 | * @test |
26 | | - * @bug 8351045 8351996 |
27 | | - * @enablePreview |
28 | | - * @comment Remove preview if ScopedValue is finalized |
| 26 | + * @bug 8351045 8351996 8358535 |
29 | 27 | * @summary tests for class-specific values |
| 28 | + * @modules java.base/java.lang:+open |
30 | 29 | * @library /test/lib |
31 | 30 | * @run junit ClassValueTest |
32 | 31 | */ |
33 | 32 |
|
34 | 33 | import java.lang.classfile.ClassFile; |
35 | 34 | import java.lang.constant.ClassDesc; |
| 35 | +import java.lang.invoke.MethodHandle; |
36 | 36 | import java.lang.invoke.MethodHandles; |
| 37 | +import java.lang.invoke.MethodType; |
37 | 38 | import java.lang.ref.WeakReference; |
38 | 39 | import java.time.Duration; |
39 | 40 | import java.time.temporal.ChronoUnit; |
40 | 41 | import java.util.ArrayList; |
41 | 42 | import java.util.Arrays; |
42 | | -import java.util.Iterator; |
43 | 43 | import java.util.List; |
44 | 44 | import java.util.concurrent.CountDownLatch; |
45 | 45 | import java.util.concurrent.ThreadLocalRandom; |
|
49 | 49 |
|
50 | 50 | import jdk.test.lib.util.ForceGC; |
51 | 51 | import org.junit.jupiter.api.Disabled; |
52 | | -import org.junit.jupiter.api.RepeatedTest; |
53 | 52 | import org.junit.jupiter.api.Test; |
54 | | -import org.junit.jupiter.api.Timeout; |
55 | 53 |
|
56 | 54 | import static org.junit.jupiter.api.Assertions.*; |
57 | 55 |
|
@@ -479,4 +477,67 @@ protected Integer computeValue(Class<?> type) { |
479 | 477 | awaitThreads(t); |
480 | 478 | assertEquals(42, clv.get(int.class), "slow computation reinstalled value"); |
481 | 479 | } |
| 480 | + |
| 481 | + // ClassValue cache invalidated and not reinstated when another |
| 482 | + // unrelated entry is removed |
| 483 | + @Test |
| 484 | + public void testCacheRefresh() throws Throwable { |
| 485 | + // Setup |
| 486 | + var lookup = MethodHandles.privateLookupIn(ClassValue.class, MethodHandles.lookup()); |
| 487 | + var classValueEntryClass = Class.forName("java.lang.ClassValue$Entry"); |
| 488 | + MethodHandle getCacheCarefully = lookup.findStatic(ClassValue.class, "getCacheCarefully", |
| 489 | + MethodType.methodType(classValueEntryClass.arrayType(), Class.class)); |
| 490 | + var classValueMapClass = Class.forName("java.lang.ClassValue$ClassValueMap"); |
| 491 | + MethodHandle probeHomeLocation = lookup.findStatic(classValueMapClass, "probeHomeLocation", |
| 492 | + MethodType.methodType(classValueEntryClass, classValueEntryClass.arrayType(), ClassValue.class)); |
| 493 | + MethodHandle match = lookup.findVirtual(ClassValue.class, "match", |
| 494 | + MethodType.methodType(boolean.class, classValueEntryClass)); |
| 495 | + |
| 496 | + // Work |
| 497 | + ClassValue<?> clv = new ClassValue<>() { |
| 498 | + @Override |
| 499 | + protected String computeValue(Class<?> type) { |
| 500 | + return ""; |
| 501 | + } |
| 502 | + }; |
| 503 | + // A class that shouldn't have arbitrary values stuffing the cache |
| 504 | + var cleanClass = clv.getClass(); |
| 505 | + clv.get(cleanClass); // create cache on clean class |
| 506 | + assertTrue(checkDirectCacheMatch( |
| 507 | + getCacheCarefully, |
| 508 | + probeHomeLocation, |
| 509 | + match, |
| 510 | + clv, |
| 511 | + cleanClass |
| 512 | + )); |
| 513 | + clv.get(int.class); |
| 514 | + clv.remove(int.class); // invalidate cache on clean class |
| 515 | + assertFalse(checkDirectCacheMatch( |
| 516 | + getCacheCarefully, |
| 517 | + probeHomeLocation, |
| 518 | + match, |
| 519 | + clv, |
| 520 | + cleanClass |
| 521 | + )); |
| 522 | + clv.get(cleanClass); |
| 523 | + assertTrue(checkDirectCacheMatch( |
| 524 | + getCacheCarefully, |
| 525 | + probeHomeLocation, |
| 526 | + match, |
| 527 | + clv, |
| 528 | + cleanClass |
| 529 | + )); |
| 530 | + } |
| 531 | + |
| 532 | + private static boolean checkDirectCacheMatch( |
| 533 | + MethodHandle getCacheCarefully, |
| 534 | + MethodHandle probeHomeLocation, |
| 535 | + MethodHandle match, |
| 536 | + ClassValue<?> clv, |
| 537 | + Class<?> cl |
| 538 | + ) throws Throwable { |
| 539 | + Object cache = getCacheCarefully.invoke(cl); |
| 540 | + Object entry = probeHomeLocation.invoke(cache, clv); |
| 541 | + return (boolean) match.invoke(clv, entry); |
| 542 | + } |
482 | 543 | } |
0 commit comments