mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2025-02-25 08:52:04 +01:00
Add support for Java 16 (#1120)
Switched from the now-unavailable ClassLoader::defineClass method to the java.lang.invoke.MethodHandles.Lookup::defineClass. This is available on Java 9+.
This commit is contained in:
parent
97972acee8
commit
7ce3f471bf
@ -141,6 +141,18 @@ public final class StructureCompiler {
|
|||||||
private static String COMPILED_CLASS = PACKAGE_NAME + "/CompiledStructureModifier";
|
private static String COMPILED_CLASS = PACKAGE_NAME + "/CompiledStructureModifier";
|
||||||
private static String FIELD_EXCEPTION_CLASS = "com/comphenix/protocol/reflect/FieldAccessException";
|
private static String FIELD_EXCEPTION_CLASS = "com/comphenix/protocol/reflect/FieldAccessException";
|
||||||
|
|
||||||
|
// On java 9+ (53.0+) CLassLoader#defineClass(String, byte[], int, int) should not be used anymore.
|
||||||
|
// It will throw warnings and on Java 16+ (60.0+), it does not work at all anymore.
|
||||||
|
private static final boolean LEGACY_CLASS_DEFINITION =
|
||||||
|
Float.parseFloat(System.getProperty("java.class.version")) < 53;
|
||||||
|
/**
|
||||||
|
* The MethodHandles.Lookup object for this compiler. Only used when using the modern defineClass strategy.
|
||||||
|
*/
|
||||||
|
private Object lookup = null;
|
||||||
|
|
||||||
|
// Used to get the MethodHandles.Lookup object on newer versions of Java.
|
||||||
|
private volatile static Method lookupMethod;
|
||||||
|
|
||||||
public static boolean attemptClassLoad = false;
|
public static boolean attemptClassLoad = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -289,36 +301,54 @@ public final class StructureCompiler {
|
|||||||
|
|
||||||
byte[] data = cw.toByteArray();
|
byte[] data = cw.toByteArray();
|
||||||
|
|
||||||
// Call the define method
|
Class<?> clazz = defineClass(data);
|
||||||
try {
|
// DEBUG CODE: Print the content of the generated class.
|
||||||
|
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
|
||||||
|
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> defineClassLegacy(byte[] data) throws InvocationTargetException, IllegalAccessException,
|
||||||
|
NoSuchMethodException {
|
||||||
if (defineMethod == null) {
|
if (defineMethod == null) {
|
||||||
Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
|
Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||||
new Class<?>[] { String.class, byte[].class, int.class, int.class });
|
new Class<?>[]{String.class, byte[].class, int.class, int.class});
|
||||||
|
|
||||||
// Awesome. Now, create and return it.
|
// Awesome. Now, create and return it.
|
||||||
defined.setAccessible(true);
|
defined.setAccessible(true);
|
||||||
defineMethod = defined;
|
defineMethod = defined;
|
||||||
}
|
}
|
||||||
|
return (Class<?>) defineMethod.invoke(loader, null, data, 0, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
private Class<?> defineClassModern(byte[] data) throws InvocationTargetException, IllegalAccessException,
|
||||||
Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length);
|
ClassNotFoundException, NoSuchMethodException {
|
||||||
|
if (defineMethod == null) {
|
||||||
|
defineMethod = Class.forName("java.lang.invoke.MethodHandles$Lookup")
|
||||||
|
.getDeclaredMethod("defineClass", byte[].class);
|
||||||
|
}
|
||||||
|
if (lookupMethod == null) {
|
||||||
|
lookupMethod = Class.forName("java.lang.invoke.MethodHandles").getDeclaredMethod("lookup");
|
||||||
|
}
|
||||||
|
if (lookup == null)
|
||||||
|
lookup = lookupMethod.invoke(null);
|
||||||
|
|
||||||
// DEBUG CODE: Print the content of the generated class.
|
return (Class<?>) defineMethod.invoke(lookup, data);
|
||||||
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
|
}
|
||||||
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
|
|
||||||
|
|
||||||
return clazz;
|
|
||||||
|
|
||||||
|
private Class<?> defineClass(byte[] data) {
|
||||||
|
try {
|
||||||
|
return LEGACY_CLASS_DEFINITION ? defineClassLegacy(data) : defineClassModern(data);
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
|
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException | ClassNotFoundException e) {
|
||||||
throw new IllegalStateException("Incompatible JVM.", e);
|
throw new IllegalStateException("Incompatible JVM.", e);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new IllegalStateException("Cannot call defineMethod - wrong JVM?", e);
|
throw new IllegalStateException("Cannot call defineMethod - wrong JVM?", e);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException("Security limitation! Cannot dynamically load class.", e);
|
throw new RuntimeException("Security limitation! Cannot dynamically load class.", e);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
throw new RuntimeException("Error occured in code generator.", e);
|
throw new RuntimeException("Error occurred in code generator.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user