Use StackWalker on Java 9+ and add compatibility layer

This commit is contained in:
filoghost 2018-08-24 18:24:13 +02:00
parent 57f7e0e9e4
commit 0ad5cdac32
11 changed files with 136 additions and 44 deletions

15
JavaCompat/pom.xml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.gmail.filoghost.holographicdisplays</groupId>
<artifactId>holographicdisplays-parent</artifactId>
<version>2.3.0-SNAPSHOT</version>
</parent>
<artifactId>holographicdisplays-javacompat</artifactId>
<name>HolographicDisplays Java Compat</name>
</project>

View File

@ -0,0 +1,20 @@
package java.lang;
import java.util.function.Function;
import java.util.stream.Stream;
public class StackWalker {
public static interface StackFrame {
StackTraceElement toStackTraceElement();
}
public static StackWalker getInstance() {
return null;
}
public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function) {
return null;
}
}

View File

@ -0,0 +1,9 @@
package java.util;
public class Optional<T> {
public T orElse(T other) {
return null;
}
}

View File

@ -0,0 +1,7 @@
package java.util.function;
public interface Function<T, R> {
R apply(T t);
}

View File

@ -0,0 +1,13 @@
package java.util.stream;
import java.util.Optional;
public interface Stream<T> {
public Stream<T> skip(long n);
public Stream<T> limit(long maxSize);
public Optional<T> findFirst();
}

View File

@ -35,6 +35,7 @@ import com.gmail.filoghost.holographicdisplays.task.StartupLoadHologramsTask;
import com.gmail.filoghost.holographicdisplays.task.WorldPlayerCounterTask; import com.gmail.filoghost.holographicdisplays.task.WorldPlayerCounterTask;
import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger; import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger;
import com.gmail.filoghost.holographicdisplays.util.NMSVersion; import com.gmail.filoghost.holographicdisplays.util.NMSVersion;
import com.gmail.filoghost.holographicdisplays.util.Utils;
import com.gmail.filoghost.holographicdisplays.util.VersionUtils; import com.gmail.filoghost.holographicdisplays.util.VersionUtils;
public class HolographicDisplays extends JavaPlugin { public class HolographicDisplays extends JavaPlugin {
@ -161,7 +162,7 @@ public class HolographicDisplays extends JavaPlugin {
if (requiredVersionError == null) { if (requiredVersionError == null) {
ProtocolLibHook protocolLibHook; ProtocolLibHook protocolLibHook;
if (VersionUtils.classExists("com.comphenix.protocol.wrappers.WrappedDataWatcher$WrappedDataWatcherObject")) { if (Utils.classExists("com.comphenix.protocol.wrappers.WrappedDataWatcher$WrappedDataWatcherObject")) {
// Only the new version contains this class // Only the new version contains this class
ConsoleLogger.log(Level.INFO, "Found ProtocolLib, using new version."); ConsoleLogger.log(Level.INFO, "Found ProtocolLib, using new version.");
protocolLibHook = new com.gmail.filoghost.holographicdisplays.bridge.protocollib.current.ProtocolLibHookImpl(); protocolLibHook = new com.gmail.filoghost.holographicdisplays.bridge.protocollib.current.ProtocolLibHookImpl();

View File

@ -36,6 +36,13 @@
<version>1.8-R0.1-SNAPSHOT</version> <version>1.8-R0.1-SNAPSHOT</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>holographicdisplays-javacompat</artifactId>
<version>2.3.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -53,15 +53,10 @@ public class Utils extends Object {
return new ArrayList<T>(); return new ArrayList<T>();
} }
public static <T> Set<T> newSet() { public static <T> Set<T> newSet() {
return new HashSet<T>(); return new HashSet<T>();
} }
@SuppressWarnings("unchecked")
public static <T> T[] listToArray(List<T> list) {
return (T[]) list.toArray();
}
public static int floor(double num) { public static int floor(double num) {
int floor = (int) num; int floor = (int) num;
@ -107,10 +102,12 @@ public class Utils extends Object {
return join(elements, separator, 0, elements.size()); return join(elements, separator, 0, elements.size());
} }
public static String sanitize(String s) { public static String sanitize(String s) {
return s != null ? s : "null"; return s != null ? s : "null";
} }
public static boolean isThereNonNull(Object... objects) { public static boolean isThereNonNull(Object... objects) {
if (objects == null) { if (objects == null) {
return false; return false;
@ -124,4 +121,14 @@ public class Utils extends Object {
return false; return false;
} }
public static boolean classExists(String className) {
try {
Class.forName(className);
return true;
} catch (Throwable t) {
return false;
}
}
} }

View File

@ -79,14 +79,4 @@ public class VersionUtils {
return isVersionGreaterEqual(reference, lowest) && isVersionLessEqual(reference, highest); return isVersionGreaterEqual(reference, lowest) && isVersionLessEqual(reference, highest);
} }
public static boolean classExists(String className) {
try {
Class.forName(className);
return true;
} catch (Throwable t) {
return false;
}
}
} }

View File

@ -1,47 +1,69 @@
package com.gmail.filoghost.holographicdisplays.util.reflection; package com.gmail.filoghost.holographicdisplays.util.reflection;
import java.lang.reflect.Method; import java.lang.StackWalker.StackFrame;
import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Stream;
import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger; import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger;
import com.gmail.filoghost.holographicdisplays.util.Utils;
public class ReflectionUtils { public class ReflectionUtils {
private static Method getStackTraceElementMethod; private static final boolean HAS_JAVA9_STACKWALKER = Utils.classExists("java.lang.StackWalker");
private static Method getStackTraceDepthMethod;
private static boolean stackTraceErrorPrinted; private static final ReflectMethod<Integer> GET_STACKTRACE_DEPTH_METHOD = new ReflectMethod<Integer>(Throwable.class, "getStackTraceDepth");
private static final ReflectMethod<StackTraceElement> GET_STACKTRACE_ELEMENT_METHOD = new ReflectMethod<StackTraceElement>(Throwable.class, "getStackTraceElement", int.class);
private static boolean stackTraceError;
/** /**
* If you only need one stack trace element this is faster than Throwable.getStackTrace()[element], * If you only need one stack trace element this is faster than Throwable.getStackTrace()[element],
* it doesn't generate the full stack trace. * it doesn't generate the full stack trace (except as fallback).
*/ */
public static StackTraceElement getStackTraceElement(int index) { public static StackTraceElement getStackTraceElement(final int index) {
// Use the faster methods only if there hasn't been any error while executing them previously
if (!stackTraceError) {
try { try {
if (getStackTraceElementMethod == null) { if (HAS_JAVA9_STACKWALKER) {
getStackTraceElementMethod = Throwable.class.getDeclaredMethod("getStackTraceElement", int.class); // Use the Java 9 StackWalker API
getStackTraceElementMethod.setAccessible(true); StackFrame result = StackWalker.getInstance().walk(new Function<Stream<StackFrame>, StackFrame>() {
}
if (getStackTraceDepthMethod == null) { @Override
getStackTraceDepthMethod = Throwable.class.getDeclaredMethod("getStackTraceDepth"); public StackFrame apply(Stream<StackFrame> stream) {
getStackTraceDepthMethod.setAccessible(true); return stream.skip(index).limit(1).findFirst().orElse(null);
} }
});
return result != null ? result.toStackTraceElement() : null;
} else {
// Use reflection to avoid generating the full stacktrace
Throwable dummyThrowable = new Throwable(); Throwable dummyThrowable = new Throwable();
int depth = (Integer) getStackTraceDepthMethod.invoke(dummyThrowable); int depth = GET_STACKTRACE_DEPTH_METHOD.invoke(dummyThrowable);
if (index < depth) { if (index < depth) {
return (StackTraceElement) getStackTraceElementMethod.invoke(new Throwable(), index); return GET_STACKTRACE_ELEMENT_METHOD.invoke(dummyThrowable, index);
} else { } else {
return null; return null;
} }
} catch (Throwable t) {
if (!stackTraceErrorPrinted) {
ConsoleLogger.log(Level.WARNING, "Unable to get a stacktrace element, please inform the developer. You will only see this error once to avoid spam.", t);
stackTraceErrorPrinted = true;
} }
} catch (Throwable t) {
if (!stackTraceError) {
ConsoleLogger.log(Level.WARNING, "Unable to get a stacktrace element, please inform the developer. You will only see this error once and a fallback method will be used.", t);
stackTraceError = true;
}
}
}
// Fallback slower method, generates the full stack trace (it should never be called if everything works as expected)
StackTraceElement[] fullStackTrace = new Throwable().getStackTrace();
if (index < fullStackTrace.length) {
return fullStackTrace[index];
} else {
return null; return null;
} }
} }
} }

View File

@ -18,6 +18,7 @@
<modules> <modules>
<module>API</module> <module>API</module>
<module>JavaCompat</module>
<module>Utils</module> <module>Utils</module>
<module>NMS/Interfaces</module> <module>NMS/Interfaces</module>
<module>NMS/v1_8_R1</module> <module>NMS/v1_8_R1</module>