浏览代码

Fixes the reflection allocator with Java 9, tested with Java 9 Early Access build 129 and OpenJDK 1.8.0 update 101

Julien Gouesse 9 年之前
父节点
当前提交
f820bbfd94
共有 1 个文件被更改,包括 64 次插入17 次删除
  1. 64 17
      jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java

+ 64 - 17
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);