Add: download and patch JavaFX before start. Closes #800.
This commit is contained in:
@@ -19,14 +19,13 @@ package org.jackhuang.hmcl;
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
|
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
|
||||||
|
import org.jackhuang.hmcl.util.VMUtils;
|
||||||
|
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -37,7 +36,6 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||||
@@ -61,15 +59,14 @@ public final class Main {
|
|||||||
|
|
||||||
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
||||||
|
|
||||||
checkJavaFX(classLoader -> {
|
VMUtils.patch();
|
||||||
try {
|
|
||||||
Class<?> c = Class.forName("org.jackhuang.hmcl.Launcher", true, classLoader);
|
checkJavaFX();
|
||||||
Method method = c.getDeclaredMethod("main");
|
|
||||||
method.invoke(null, (Object) args);
|
// Fix title bar not displaying in GTK systems
|
||||||
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
System.setProperty("jdk.gtk.version", "2");
|
||||||
throw new InternalError(e);
|
|
||||||
}
|
Launcher.main(args);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkDirectoryPath() {
|
private static void checkDirectoryPath() {
|
||||||
@@ -81,9 +78,9 @@ public final class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkJavaFX(Consumer<ClassLoader> runnable) {
|
private static void checkJavaFX() {
|
||||||
try {
|
try {
|
||||||
SelfDependencyPatcher.runInJavaFxEnvironment(runnable);
|
SelfDependencyPatcher.patch();
|
||||||
} catch (SelfDependencyPatcher.PatchException e) {
|
} catch (SelfDependencyPatcher.PatchException e) {
|
||||||
LOG.log(Level.SEVERE, "unable to patch JVM", e);
|
LOG.log(Level.SEVERE, "unable to patch JVM", e);
|
||||||
showErrorAndExit(i18n("fatal.javafx.missing"));
|
showErrorAndExit(i18n("fatal.javafx.missing"));
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ public final class UpdateHandler {
|
|||||||
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
|
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
|
||||||
for (int i = 0; i < stacktrace.length; i++) {
|
for (int i = 0; i < stacktrace.length; i++) {
|
||||||
StackTraceElement element = stacktrace[i];
|
StackTraceElement element = stacktrace[i];
|
||||||
if (Main.class.getName().equals(element.getClassName())) {
|
if (Main.class.getName().equals(element.getClassName()) && "main".equals(element.getMethodName())) {
|
||||||
// we've reached the main method
|
// we've reached the main method
|
||||||
return i + 1 != stacktrace.length;
|
return i + 1 != stacktrace.length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import javax.swing.*;
|
|||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@@ -19,7 +18,6 @@ import java.util.List;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import java.util.concurrent.ForkJoinTask;
|
import java.util.concurrent.ForkJoinTask;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static java.lang.Class.forName;
|
import static java.lang.Class.forName;
|
||||||
@@ -89,15 +87,12 @@ public class SelfDependencyPatcher {
|
|||||||
/**
|
/**
|
||||||
* Patch in any missing dependencies, if any.
|
* Patch in any missing dependencies, if any.
|
||||||
*/
|
*/
|
||||||
public static void runInJavaFxEnvironment(Consumer<ClassLoader> runnable) throws PatchException, IncompatibleVersionException {
|
public static void patch() throws PatchException, IncompatibleVersionException {
|
||||||
if (CURRENT_JAVA.getParsedVersion() > 8) {
|
|
||||||
patchReflectionFilters();
|
|
||||||
}
|
|
||||||
// Do nothing if JavaFX is detected
|
// Do nothing if JavaFX is detected
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
forName("javafx.application.Application");
|
forName("javafx.application.Application");
|
||||||
runnable.accept(SelfDependencyPatcher.class.getClassLoader());
|
return;
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
} catch (UnsupportedClassVersionError error) {
|
} catch (UnsupportedClassVersionError error) {
|
||||||
@@ -140,33 +135,12 @@ public class SelfDependencyPatcher {
|
|||||||
loadFromCache();
|
loadFromCache();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new PatchException("Failed to load JavaFX cache", ex);
|
throw new PatchException("Failed to load JavaFX cache", ex);
|
||||||
} catch (ReflectiveOperationException ex) {
|
} catch (ReflectiveOperationException | NoClassDefFoundError ex) {
|
||||||
throw new PatchException("Failed to add dependencies to classpath!", ex);
|
throw new PatchException("Failed to add dependencies to classpath!", ex);
|
||||||
}
|
}
|
||||||
LOG.info(" - Done!");
|
LOG.info(" - Done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Inject them into the current classpath.
|
|
||||||
// *
|
|
||||||
// * @throws IOException When the locally cached dependency urls cannot be resolved.
|
|
||||||
// * @throws ReflectiveOperationException When the call to add these urls to the system classpath failed.
|
|
||||||
// */
|
|
||||||
// private static void loadFromCache(Consumer<ClassLoader> runnable) throws IOException, ReflectiveOperationException {
|
|
||||||
// LOG.info(" - Loading dependencies...");
|
|
||||||
// // Get Jar URLs
|
|
||||||
// List<URL> jarUrls = new ArrayList<>();
|
|
||||||
// Files.walk(DEPENDENCIES_DIR_PATH).forEach(path -> {
|
|
||||||
// try {
|
|
||||||
// jarUrls.add(path.toUri().toURL());
|
|
||||||
// } catch (MalformedURLException ex) {
|
|
||||||
// LOG.log(Level.WARNING, "Failed to convert '" + path.toFile().getAbsolutePath() + "' to URL", ex);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// ClassLoader classLoader = new URLClassLoader(jarUrls.toArray(new URL[0]), SelfDependencyPatcher.class.getClassLoader());
|
|
||||||
// runnable.accept(classLoader);
|
|
||||||
// }
|
|
||||||
/**
|
/**
|
||||||
* Inject them into the current classpath.
|
* Inject them into the current classpath.
|
||||||
*
|
*
|
||||||
@@ -312,51 +286,6 @@ public class SelfDependencyPatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Patches reflection filters.
|
|
||||||
*/
|
|
||||||
private static void patchReflectionFilters() {
|
|
||||||
Class<?> klass;
|
|
||||||
try {
|
|
||||||
klass = Class.forName("jdk.internal.reflect.Reflection",
|
|
||||||
true, null);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
throw new RuntimeException("Unable to locate 'jdk.internal.reflect.Reflection' class", ex);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Field[] fields;
|
|
||||||
try {
|
|
||||||
Method m = Class.class.getDeclaredMethod("getDeclaredFieldsImpl");
|
|
||||||
ReflectionHelper.setAccessible(m);
|
|
||||||
fields = (Field[]) m.invoke(klass);
|
|
||||||
} catch (NoSuchMethodException | InvocationTargetException ex) {
|
|
||||||
try {
|
|
||||||
Method m = Class.class.getDeclaredMethod("getDeclaredFields0", Boolean.TYPE);
|
|
||||||
ReflectionHelper.setAccessible(m);
|
|
||||||
fields = (Field[]) m.invoke(klass, false);
|
|
||||||
} catch (InvocationTargetException | NoSuchMethodException ex1) {
|
|
||||||
ex.addSuppressed(ex1);
|
|
||||||
throw new RuntimeException("Unable to get all class fields", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int c = 0;
|
|
||||||
for (Field field : fields) {
|
|
||||||
String name = field.getName();
|
|
||||||
if ("fieldFilterMap".equals(name) || "methodFilterMap".equals(name)) {
|
|
||||||
ReflectionHelper.setAccessible(field);
|
|
||||||
field.set(null, new HashMap<>(0));
|
|
||||||
if (++c == 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("One of field patches did not apply properly. " +
|
|
||||||
"Expected to patch two fields, but patched: " + c);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException ex) {
|
|
||||||
throw new RuntimeException("Unable to patch reflection filters", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PatchException extends Exception {
|
public static class PatchException extends Exception {
|
||||||
PatchException(String message, Throwable cause) {
|
PatchException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
|||||||
16
HMCLCore/src/main/java/java/lang/Module.java
Normal file
16
HMCLCore/src/main/java/java/lang/Module.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package java.lang;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy java compatibility class
|
||||||
|
*
|
||||||
|
* @author Matt
|
||||||
|
*/
|
||||||
|
public abstract class Module {
|
||||||
|
|
||||||
|
//CHECKSTYLE:OFF
|
||||||
|
public ModuleLayer getLayer() { throw new UnsupportedOperationException(); }
|
||||||
|
public Set<String> getPackages() { throw new UnsupportedOperationException(); }
|
||||||
|
//CHECKSTYLE:ON
|
||||||
|
}
|
||||||
20
HMCLCore/src/main/java/java/lang/ModuleLayer.java
Normal file
20
HMCLCore/src/main/java/java/lang/ModuleLayer.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package java.lang;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy java compatibility class
|
||||||
|
*
|
||||||
|
* @author xxDark
|
||||||
|
*/
|
||||||
|
public abstract class ModuleLayer {
|
||||||
|
|
||||||
|
//CHECKSTYLE:OFF
|
||||||
|
public Set<Module> modules() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
public static ModuleLayer boot() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
//CHECKSTYLE:ON
|
||||||
|
}
|
||||||
17
HMCLCore/src/main/java/java/lang/module/ModuleFinder.java
Normal file
17
HMCLCore/src/main/java/java/lang/module/ModuleFinder.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package java.lang.module;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy java compatibility class
|
||||||
|
*
|
||||||
|
* @author xxDark
|
||||||
|
*/
|
||||||
|
public interface ModuleFinder {
|
||||||
|
//CHECKSTYLE:OFF
|
||||||
|
static ModuleFinder ofSystem() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
Set<ModuleReference> findAll();
|
||||||
|
//CHECKSTYLE:ON
|
||||||
|
}
|
||||||
18
HMCLCore/src/main/java/java/lang/module/ModuleReader.java
Normal file
18
HMCLCore/src/main/java/java/lang/module/ModuleReader.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package java.lang.module;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy java compatibility class
|
||||||
|
*
|
||||||
|
* @author xxDark
|
||||||
|
*/
|
||||||
|
public interface ModuleReader extends Closeable {
|
||||||
|
//CHECKSTYLE:OFF
|
||||||
|
Stream<String> list() throws IOException;
|
||||||
|
@Override
|
||||||
|
void close() throws IOException;
|
||||||
|
//CHECKSTYLE:ON
|
||||||
|
}
|
||||||
14
HMCLCore/src/main/java/java/lang/module/ModuleReference.java
Normal file
14
HMCLCore/src/main/java/java/lang/module/ModuleReference.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package java.lang.module;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy java compatibility class
|
||||||
|
*
|
||||||
|
* @author xxDark
|
||||||
|
*/
|
||||||
|
public abstract class ModuleReference {
|
||||||
|
//CHECKSTYLE:OFF
|
||||||
|
public abstract ModuleReader open() throws IOException;
|
||||||
|
//CHECKSTYLE:ON
|
||||||
|
}
|
||||||
@@ -167,6 +167,10 @@ public final class ModInfo implements Comparable<ModInfo> {
|
|||||||
private final String text;
|
private final String text;
|
||||||
private final String color;
|
private final String color;
|
||||||
|
|
||||||
|
public Part(String text) {
|
||||||
|
this(text, "");
|
||||||
|
}
|
||||||
|
|
||||||
public Part(String text, String color) {
|
public Part(String text, String color) {
|
||||||
this.text = Objects.requireNonNull(text);
|
this.text = Objects.requireNonNull(text);
|
||||||
this.color = Objects.requireNonNull(color);
|
this.color = Objects.requireNonNull(color);
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.jackhuang.hmcl.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for dealing with class-file loading/parsing.
|
||||||
|
*
|
||||||
|
* @author Matt
|
||||||
|
*/
|
||||||
|
public class ClassUtils {
|
||||||
|
/**
|
||||||
|
* The offset from which a version and the version constant value is. For example, Java 8 is 52 <i>(44 + 8)</i>.
|
||||||
|
*/
|
||||||
|
public static final int VERSION_OFFSET = 44;
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package org.jackhuang.hmcl.util;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package-private util to deal with modules.
|
||||||
|
*
|
||||||
|
* @author xxDark
|
||||||
|
*/
|
||||||
|
final class Java9Util {
|
||||||
|
|
||||||
|
private static final MethodHandle CLASS_MODULE;
|
||||||
|
private static final MethodHandle CLASS_LOADER_MDOULE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deny all constructions.
|
||||||
|
*/
|
||||||
|
private Java9Util() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param klass {@link Class} to get module from.
|
||||||
|
* @return {@link Module} of the class.
|
||||||
|
*/
|
||||||
|
static Module getClassModule(Class<?> klass) {
|
||||||
|
try {
|
||||||
|
return (Module) CLASS_MODULE.invokeExact(klass);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// That should never happen.
|
||||||
|
throw new AssertionError(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param loader {@link ClassLoader} to get module from.
|
||||||
|
* @return {@link Module} of the class.
|
||||||
|
*/
|
||||||
|
static Module getLoaderModule(ClassLoader loader) {
|
||||||
|
try {
|
||||||
|
return (Module) CLASS_LOADER_MDOULE.invokeExact(loader);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// That should never happen.
|
||||||
|
throw new AssertionError(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP");
|
||||||
|
field.setAccessible(true);
|
||||||
|
MethodHandles.publicLookup();
|
||||||
|
Lookup lookup = (Lookup) field.get(null);
|
||||||
|
MethodType type = MethodType.methodType(Module.class);
|
||||||
|
CLASS_MODULE = lookup.findVirtual(Class.class, "getModule", type);
|
||||||
|
CLASS_LOADER_MDOULE = lookup.findVirtual(ClassLoader.class, "getUnnamedModule", type);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | NoSuchFieldException ex) {
|
||||||
|
throw new ExceptionInInitializerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
270
HMCLCore/src/main/java/org/jackhuang/hmcl/util/VMUtils.java
Normal file
270
HMCLCore/src/main/java/org/jackhuang/hmcl/util/VMUtils.java
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
package org.jackhuang.hmcl.util;
|
||||||
|
|
||||||
|
import com.sun.javafx.application.PlatformImpl;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependent and non-dependent platform utilities for VM.
|
||||||
|
*
|
||||||
|
* @author xxDark
|
||||||
|
*/
|
||||||
|
public final class VMUtils {
|
||||||
|
private static int vmVersion = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deny all constructions.
|
||||||
|
*/
|
||||||
|
private VMUtils() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends URL to the {@link URLClassLoader}.
|
||||||
|
*
|
||||||
|
* @param cl the classloader to add {@link URL} for.
|
||||||
|
* @param url the {@link URL} to add.
|
||||||
|
*/
|
||||||
|
public static void addURL(ClassLoader cl, URL url) {
|
||||||
|
if (cl instanceof URLClassLoader) {
|
||||||
|
addURL0(cl, url);
|
||||||
|
} else {
|
||||||
|
addURL1(cl, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return running VM version.
|
||||||
|
*/
|
||||||
|
public static int getVmVersion() {
|
||||||
|
if (vmVersion < 0) {
|
||||||
|
// Check for class version, ez
|
||||||
|
String property = System.getProperty("java.class.version", "");
|
||||||
|
if (!property.isEmpty())
|
||||||
|
return vmVersion = (int) (Float.parseFloat(property) - ClassUtils.VERSION_OFFSET);
|
||||||
|
// Odd, not found. Try the spec version
|
||||||
|
Logging.LOG.warning("Using fallback vm-version fetch, no value for 'java.class.version'");
|
||||||
|
property = System.getProperty("java.vm.specification.version", "");
|
||||||
|
if (property.contains("."))
|
||||||
|
return vmVersion = (int) Float.parseFloat(property.substring(property.indexOf('.') + 1));
|
||||||
|
else if (!property.isEmpty())
|
||||||
|
return vmVersion = Integer.parseInt(property);
|
||||||
|
// Very odd
|
||||||
|
Logging.LOG.warning("Fallback vm-version fetch failed, defaulting to 8");
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return vmVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addURL0(ClassLoader loader, URL url) {
|
||||||
|
Method method;
|
||||||
|
try {
|
||||||
|
method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||||
|
} catch (NoSuchMethodException ex) {
|
||||||
|
throw new RuntimeException("No 'addURL' method in java.net.URLClassLoader", ex);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
try {
|
||||||
|
method.invoke(loader, url);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalStateException("'addURL' became inaccessible", ex);
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
throw new RuntimeException("Error adding URL", ex.getTargetException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addURL1(ClassLoader loader, URL url) {
|
||||||
|
Class<?> currentClass = loader.getClass();
|
||||||
|
do {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
field = currentClass.getDeclaredField("ucp");
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object ucp;
|
||||||
|
try {
|
||||||
|
ucp = field.get(loader);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalStateException("'ucp' became inaccessible", ex);
|
||||||
|
}
|
||||||
|
String className;
|
||||||
|
if (getVmVersion() < 9) {
|
||||||
|
className = "sun.misc.URLClassPath";
|
||||||
|
} else {
|
||||||
|
className = "jdk.internal.misc.URLClassPath";
|
||||||
|
}
|
||||||
|
Method method;
|
||||||
|
try {
|
||||||
|
method = Class.forName(className, true, null).getDeclaredMethod("addURL", URL.class);
|
||||||
|
} catch (NoSuchMethodException ex) {
|
||||||
|
throw new RuntimeException("No 'addURL' method in " + className, ex);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
throw new RuntimeException(className + " was not found", ex);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
try {
|
||||||
|
method.invoke(ucp, url);
|
||||||
|
break;
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalStateException("'addURL' became inaccessible", ex);
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
throw new RuntimeException("Error adding URL", ex.getTargetException());
|
||||||
|
}
|
||||||
|
} while ((currentClass=currentClass.getSuperclass()) != Object.class);
|
||||||
|
throw new IllegalArgumentException("No 'ucp' field in " + loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes {@link URLClassLoader}.
|
||||||
|
*
|
||||||
|
* @param loader
|
||||||
|
* Loader to close.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* When I/O error occurs.
|
||||||
|
*/
|
||||||
|
public static void close(URLClassLoader loader) throws IOException {
|
||||||
|
loader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets parent class loader.
|
||||||
|
*
|
||||||
|
* @param loader
|
||||||
|
* Loader to change parent for.
|
||||||
|
* @param parent
|
||||||
|
* New parent loader.
|
||||||
|
*/
|
||||||
|
public static void setParent(ClassLoader loader, ClassLoader parent) {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
field = ClassLoader.class.getDeclaredField("parent");
|
||||||
|
} catch (NoSuchFieldException ex) {
|
||||||
|
throw new RuntimeException("No 'parent' field in java.lang.ClassLoader", ex);
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
try {
|
||||||
|
field.set(loader, parent);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalStateException("'parent' became inaccessible", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes toolkit.
|
||||||
|
*/
|
||||||
|
public static void tkIint() {
|
||||||
|
if (getVmVersion() < 9) {
|
||||||
|
PlatformImpl.startup(() -> {});
|
||||||
|
} else {
|
||||||
|
Method m;
|
||||||
|
try {
|
||||||
|
m = Platform.class.getDeclaredMethod("startup", Runnable.class);
|
||||||
|
} catch (NoSuchMethodException ex) {
|
||||||
|
throw new RuntimeException("javafx.application.Platform.startup(Runnable) is missing", ex);
|
||||||
|
}
|
||||||
|
m.setAccessible(true);
|
||||||
|
try {
|
||||||
|
m.invoke(null, (Runnable) () -> {});
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalStateException("'startup' became inaccessible", ex);
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
throw new RuntimeException("Unable to initialize toolkit", ex.getTargetException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patches JDK stuff.
|
||||||
|
*/
|
||||||
|
public static void patch() {
|
||||||
|
if (getVmVersion() > 8) {
|
||||||
|
openPackages();
|
||||||
|
patchReflectionFilters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens all packages.
|
||||||
|
*/
|
||||||
|
private static void openPackages() {
|
||||||
|
try {
|
||||||
|
Method export = Module.class.getDeclaredMethod("implAddOpens", String.class);
|
||||||
|
export.setAccessible(true);
|
||||||
|
HashSet<Module> modules = new HashSet<>();
|
||||||
|
Class<?> classBase = VMUtils.class;
|
||||||
|
Module base = Java9Util.getClassModule(classBase);
|
||||||
|
if (base.getLayer() != null)
|
||||||
|
modules.addAll(base.getLayer().modules());
|
||||||
|
modules.addAll(ModuleLayer.boot().modules());
|
||||||
|
for (ClassLoader cl = classBase.getClassLoader(); cl != null; cl = cl.getParent()) {
|
||||||
|
modules.add(Java9Util.getLoaderModule(cl));
|
||||||
|
}
|
||||||
|
for (Module module : modules) {
|
||||||
|
for (String name : module.getPackages()) {
|
||||||
|
try {
|
||||||
|
export.invoke(module, name);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logging.LOG.log(Level.SEVERE, "Could not export package " + name + " in module " + module, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logging.LOG.log(Level.SEVERE, "Could not export packages", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patches reflection filters.
|
||||||
|
*/
|
||||||
|
private static void patchReflectionFilters() {
|
||||||
|
Class<?> klass;
|
||||||
|
try {
|
||||||
|
klass = Class.forName("jdk.internal.reflect.Reflection",
|
||||||
|
true, null);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
throw new RuntimeException("Unable to locate 'jdk.internal.reflect.Reflection' class", ex);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Field[] fields;
|
||||||
|
try {
|
||||||
|
Method m = Class.class.getDeclaredMethod("getDeclaredFieldsImpl");
|
||||||
|
m.setAccessible(true);
|
||||||
|
fields = (Field[]) m.invoke(klass);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException ex) {
|
||||||
|
try {
|
||||||
|
Method m = Class.class.getDeclaredMethod("getDeclaredFields0", Boolean.TYPE);
|
||||||
|
m.setAccessible(true);
|
||||||
|
fields = (Field[]) m.invoke(klass, false);
|
||||||
|
} catch (InvocationTargetException | NoSuchMethodException ex1) {
|
||||||
|
ex.addSuppressed(ex1);
|
||||||
|
throw new RuntimeException("Unable to get all class fields", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int c = 0;
|
||||||
|
for (Field field : fields) {
|
||||||
|
String name = field.getName();
|
||||||
|
if ("fieldFilterMap".equals(name) || "methodFilterMap".equals(name)) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(null, new HashMap<>(0));
|
||||||
|
if (++c == 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("One of field patches did not apply properly. " +
|
||||||
|
"Expected to patch two fields, but patched: " + c);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new RuntimeException("Unable to patch reflection filters", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user