From f820bbfd94c16021f8a360796ff499f0d1ce2d28 Mon Sep 17 00:00:00 2001 From: Julien Gouesse Date: Sun, 7 Aug 2016 14:31:43 +0200 Subject: [PATCH] Fixes the reflection allocator with Java 9, tested with Java 9 Early Access build 129 and OpenJDK 1.8.0 update 101 --- .../com/jme3/util/ReflectionAllocator.java | 81 +++++++++++++++---- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java b/jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java index de7e75bcb..276241e1a 100644 --- a/jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java +++ b/jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java @@ -40,7 +40,7 @@ import java.util.logging.Logger; /** * This class contains the reflection based way to remove DirectByteBuffers in - * java < 9, allocation is done via ByteBuffer.allocateDirect + * java, allocation is done via ByteBuffer.allocateDirect */ public final class ReflectionAllocator implements BufferAllocator { private static Method cleanerMethod = null; @@ -54,7 +54,7 @@ public final class ReflectionAllocator implements BufferAllocator { cleanMethod = loadMethod("sun.misc.Cleaner", "clean"); viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer"); if (viewedBufferMethod == null) { - // They changed the name in Java 7 (???) + // They changed the name in Java 7 viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment"); } @@ -72,7 +72,7 @@ public final class ReflectionAllocator implements BufferAllocator { private static Method loadMethod(String className, String methodName) { try { Method method = Class.forName(className).getMethod(methodName); - method.setAccessible(true); + method.setAccessible(true);// according to the Java documentation, by default, a reflected object is not accessible return method; } catch (NoSuchMethodException ex) { return null; // the method was not found @@ -80,6 +80,12 @@ public final class ReflectionAllocator implements BufferAllocator { return null; // setAccessible not allowed by security policy } catch (ClassNotFoundException ex) { return null; // the direct buffer implementation was not found + } catch (Throwable t) { + if (t.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { + return null;// the class is in an exported module + } else { + throw t; + } } } @@ -96,20 +102,61 @@ public final class ReflectionAllocator implements BufferAllocator { if (freeMethod != null) { freeMethod.invoke(toBeDestroyed); } else { - Object cleaner = cleanerMethod.invoke(toBeDestroyed); - if (cleaner != null) { - cleanMethod.invoke(cleaner); - } else { - // Try the alternate approach of getting the viewed buffer - // first - Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); - if (viewedBuffer != null) { - destroyDirectBuffer((Buffer) viewedBuffer); - } else { - Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, - "Buffer cannot be destroyed: {0}", toBeDestroyed); - } - } + //TODO load the methods only once, store them into a cache (only for Java >= 9) + Method localCleanerMethod; + if (cleanerMethod == null) { + localCleanerMethod = loadMethod(toBeDestroyed.getClass().getName(), "cleaner"); + } else { + localCleanerMethod = cleanerMethod; + } + if (localCleanerMethod == null) { + Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, + "Buffer cannot be destroyed: {0}", toBeDestroyed); + } else { + Object cleaner = localCleanerMethod.invoke(toBeDestroyed); + if (cleaner != null) { + Method localCleanMethod; + if (cleanMethod == null) { + if (cleaner instanceof Runnable) { + // jdk.internal.ref.Cleaner implements Runnable in Java 9 + localCleanMethod = loadMethod(Runnable.class.getName(), "run"); + } else { + // sun.misc.Cleaner does not implement Runnable in Java < 9 + localCleanMethod = loadMethod(cleaner.getClass().getName(), "clean"); + } + } else { + localCleanMethod = cleanMethod; + } + if (localCleanMethod == null) { + Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, + "Buffer cannot be destroyed: {0}", toBeDestroyed); + } else { + localCleanMethod.invoke(cleaner); + } + } else { + Method localViewedBufferMethod; + if (viewedBufferMethod == null) { + localViewedBufferMethod = loadMethod(toBeDestroyed.getClass().getName(), "viewedBuffer"); + } else { + localViewedBufferMethod = viewedBufferMethod; + } + if (localViewedBufferMethod == null) { + Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, + "Buffer cannot be destroyed: {0}", toBeDestroyed); + } else { + // Try the alternate approach of getting the viewed + // buffer + // first + Object viewedBuffer = localViewedBufferMethod.invoke(toBeDestroyed); + if (viewedBuffer != null) { + destroyDirectBuffer((Buffer) viewedBuffer); + } else { + Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, + "Buffer cannot be destroyed: {0}", toBeDestroyed); + } + } + } + } } } catch (IllegalAccessException ex) { Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);