Skip to content

Commit d378ebf

Browse files
committed
Prep for incubator feature for releasing ByteBuffer using MemorySegment
1 parent 754e2a4 commit d378ebf

1 file changed

Lines changed: 95 additions & 44 deletions

File tree

src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java

Lines changed: 95 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,20 @@
5050
* File utilities.
5151
*/
5252
public final class FileUtils {
53+
/** The DirectByteBuffer.cleaner() method. */
54+
private static Method directByteBufferCleanerMethod;
5355

54-
/** The clean() method. */
55-
private static Method cleanMethod;
56+
/** The Cleaner.clean() method. */
57+
private static Method cleanerCleanMethod;
58+
59+
// /** The jdk.incubator.foreign.MemorySegment class (JDK14+). */
60+
// private static Class<?> memorySegmentClass;
61+
//
62+
// /** The jdk.incubator.foreign.MemorySegment.ofByteBuffer method (JDK14+). */
63+
// private static Method memorySegmentOfByteBufferMethod;
64+
//
65+
// /** The jdk.incubator.foreign.MemorySegment.ofByteBuffer method (JDK14+). */
66+
// private static Method memorySegmentCloseMethod;
5667

5768
/** The attachment() method. */
5869
private static Method attachmentMethod;
@@ -425,46 +436,66 @@ public static String getParentDirPath(final String path) {
425436
private static void lookupCleanMethodPrivileged() {
426437
if (VersionFinder.JAVA_MAJOR_VERSION < 9) {
427438
try {
428-
// See: https://stackoverflow.com/a/19447758/3950982
429-
cleanMethod = Class.forName("sun.misc.Cleaner").getMethod("clean");
430-
cleanMethod.setAccessible(true);
431-
attachmentMethod = Class.forName("sun.nio.ch.DirectBuffer").getMethod("attachment");
439+
// See:
440+
// https://stackoverflow.com/a/19447758/3950982
441+
cleanerCleanMethod = Class.forName("sun.misc.Cleaner").getDeclaredMethod("clean");
442+
cleanerCleanMethod.setAccessible(true);
443+
final Class<?> directByteBufferClass = Class.forName("sun.nio.ch.DirectBuffer");
444+
directByteBufferCleanerMethod = directByteBufferClass.getDeclaredMethod("cleaner");
445+
attachmentMethod = directByteBufferClass.getMethod("attachment");
432446
attachmentMethod.setAccessible(true);
433447
} catch (final SecurityException e) {
434448
throw ClassGraphException.newClassGraphException(
435-
"You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\"), "
436-
+ "RuntimePermission(\"accessClassInPackage.sun.nio.ch\"), "
449+
"You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") "
437450
+ "and ReflectPermission(\"suppressAccessChecks\")",
438451
e);
439452
} catch (final ReflectiveOperationException | LinkageError e) {
440453
// Ignore
441454
}
442455
} else {
443-
// In JDK9+, calling sun.misc.Cleaner.clean() gives a reflection warning on stderr,
444-
// so we need to call Unsafe.theUnsafe.invokeCleaner(byteBuffer) instead, which makes
445-
// the same call, but does not print the reflection warning.
446-
try {
447-
Class<?> unsafeClass;
456+
boolean jdk14Success = false;
457+
// // TODO: This feature is in incubation now -- enable after it leaves incubation.
458+
// // To enable this feature, need to:
459+
// // -- add whatever the "jdk.incubator.foreign" module name is replaced with to <Import-Package>
460+
// // in pom.xml, as an optional dependency
461+
// // -- add the same module name to module-info.java as a "requires static" optional dependency
462+
// // -- build two versions of module.java: the existing one, for --release=9, and a new version,
463+
// // for --release=15 (or whatever the final release version ends up being when the feature is
464+
// // moved out of incubation).
465+
// try {
466+
// // JDK 14+ Invoke MemorySegment.ofByteBuffer(myByteBuffer).close()
467+
// // https://stackoverflow.com/a/26777380/3950982
468+
// memorySegmentClass = Class.forName("jdk.incubator.foreign.MemorySegment");
469+
// memorySegmentCloseMethod = AutoCloseable.class.getDeclaredMethod("close");
470+
// memorySegmentOfByteBufferMethod = memorySegmentClass.getMethod("ofByteBuffer",
471+
// ByteBuffer.class);
472+
// jdk14Success = true;
473+
// } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e1) {
474+
// // Fall through
475+
// }
476+
if (!jdk14Success) { // In JDK9+, calling sun.misc.Cleaner.clean() gives a reflection warning on stderr,
477+
// so we need to call Unsafe.theUnsafe.invokeCleaner(byteBuffer) instead, which makes
478+
// the same call, but does not print the reflection warning.
448479
try {
449-
unsafeClass = Class.forName("sun.misc.Unsafe");
450-
} catch (final ReflectiveOperationException | LinkageError e) {
451-
// jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,
452-
// but that method should be added if sun.misc.Unsafe is removed.
453-
unsafeClass = Class.forName("jdk.internal.misc.Unsafe");
480+
Class<?> unsafeClass;
481+
try {
482+
unsafeClass = Class.forName("sun.misc.Unsafe");
483+
} catch (final ReflectiveOperationException | LinkageError e) {
484+
throw ClassGraphException.newClassGraphException("Could not get class sun.misc.Unsafe", e);
485+
}
486+
final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
487+
theUnsafeField.setAccessible(true);
488+
theUnsafe = theUnsafeField.get(null);
489+
cleanerCleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class);
490+
cleanerCleanMethod.setAccessible(true);
491+
} catch (final SecurityException e) {
492+
throw ClassGraphException.newClassGraphException(
493+
"You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") "
494+
+ "and ReflectPermission(\"suppressAccessChecks\")",
495+
e);
496+
} catch (final ReflectiveOperationException | LinkageError ex) {
497+
// Ignore
454498
}
455-
final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
456-
theUnsafeField.setAccessible(true);
457-
theUnsafe = theUnsafeField.get(null);
458-
cleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class);
459-
cleanMethod.setAccessible(true);
460-
} catch (final SecurityException e) {
461-
throw ClassGraphException.newClassGraphException(
462-
"You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\"), "
463-
+ "RuntimePermission(\"accessClassInPackage.jdk.internal.misc\") "
464-
+ "and ReflectPermission(\"suppressAccessChecks\")",
465-
e);
466-
} catch (final ReflectiveOperationException | LinkageError ex) {
467-
// Ignore
468499
}
469500
}
470501
}
@@ -489,13 +520,11 @@ public Object run() {
489520
* @return true if successful
490521
*/
491522
private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuffer, final LogNode log) {
523+
if (!byteBuffer.isDirect()) {
524+
// Nothing to do
525+
return true;
526+
}
492527
try {
493-
if (cleanMethod == null) {
494-
if (log != null) {
495-
log.log("Could not unmap ByteBuffer, cleanMethod == null");
496-
}
497-
return false;
498-
}
499528
if (VersionFinder.JAVA_MAJOR_VERSION < 9) {
500529
if (attachmentMethod == null) {
501530
if (log != null) {
@@ -512,46 +541,68 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff
512541
return false;
513542
}
514543
// Invoke ((DirectBuffer) byteBuffer).cleaner().clean()
515-
final Method cleanerMethod = byteBuffer.getClass().getMethod("cleaner");
516-
if (cleanerMethod == null) {
544+
if (directByteBufferCleanerMethod == null) {
517545
if (log != null) {
518546
log.log("Could not unmap ByteBuffer, cleanerMethod == null");
519547
}
520548
return false;
521549
}
522550
try {
523-
cleanerMethod.setAccessible(true);
551+
directByteBufferCleanerMethod.setAccessible(true);
524552
} catch (final Exception e) {
525553
if (log != null) {
526554
log.log("Could not unmap ByteBuffer, cleanerMethod.setAccessible(true) failed");
527555
}
528556
return false;
529557
}
530-
final Object cleaner = cleanerMethod.invoke(byteBuffer);
531-
if (cleaner == null) {
558+
final Object cleanerInstance = directByteBufferCleanerMethod.invoke(byteBuffer);
559+
if (cleanerInstance == null) {
532560
if (log != null) {
533561
log.log("Could not unmap ByteBuffer, cleaner == null");
534562
}
535563
return false;
536564
}
565+
if (cleanerCleanMethod == null) {
566+
if (log != null) {
567+
log.log("Could not unmap ByteBuffer, cleanMethod == null");
568+
}
569+
return false;
570+
}
537571
try {
538-
cleanMethod.invoke(cleaner);
572+
cleanerCleanMethod.invoke(cleanerInstance);
539573
return true;
540574
} catch (final Exception e) {
541575
if (log != null) {
542576
log.log("Could not unmap ByteBuffer, cleanMethod.invoke(cleaner) failed: " + e);
543577
}
544578
return false;
545579
}
580+
// } else if (memorySegmentOfByteBufferMethod != null) {
581+
// // JDK 14+
582+
// final Object memorySegment = memorySegmentOfByteBufferMethod.invoke(null, byteBuffer);
583+
// if (memorySegment == null) {
584+
// if (log != null) {
585+
// log.log("Got null MemorySegment, could not unmap ByteBuffer");
586+
// }
587+
// return false;
588+
// }
589+
// memorySegmentCloseMethod.invoke(memorySegment);
590+
// return true;
546591
} else {
547592
if (theUnsafe == null) {
548593
if (log != null) {
549594
log.log("Could not unmap ByteBuffer, theUnsafe == null");
550595
}
551596
return false;
552597
}
598+
if (cleanerCleanMethod == null) {
599+
if (log != null) {
600+
log.log("Could not unmap ByteBuffer, cleanMethod == null");
601+
}
602+
return false;
603+
}
553604
try {
554-
cleanMethod.invoke(theUnsafe, byteBuffer);
605+
cleanerCleanMethod.invoke(theUnsafe, byteBuffer);
555606
return true;
556607
} catch (final IllegalArgumentException e) {
557608
// Buffer is a duplicate or slice

0 commit comments

Comments
 (0)