mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-02-05 16:44:47 +08:00
Compatible with Java 16.
This commit is contained in:
parent
c1596443f5
commit
b521c5b76a
@ -81,7 +81,20 @@ def repack(File file) {
|
||||
new JarOutputStream(file.newOutputStream()).withCloseable { unpacker.unpack(new ByteArrayInputStream(packed.toByteArray()), it) }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
java11 {
|
||||
java {
|
||||
srcDirs = ['src/main/java11']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
into('META-INF/versions/11') {
|
||||
from sourceSets.java11.output
|
||||
exclude "java/**"
|
||||
exclude "jdk/**"
|
||||
}
|
||||
manifest {
|
||||
attributes 'Created-By': 'Copyright(c) 2013-2020 huangyuhui.',
|
||||
'Main-Class': mainClassName,
|
||||
@ -99,6 +112,7 @@ jar {
|
||||
'javafx.graphics/com.sun.javafx.stage'
|
||||
].join(" "),
|
||||
'Add-Exports': [
|
||||
'java.base/jdk.internal.loader',
|
||||
'javafx.controls/com.sun.javafx.scene.control.behavior',
|
||||
'javafx.controls/javafx.scene.control.skin',
|
||||
'javafx.controls/com.sun.javafx.scene.control',
|
||||
|
@ -19,7 +19,6 @@ package org.jackhuang.hmcl;
|
||||
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
|
||||
import org.jackhuang.hmcl.util.VMUtils;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import javax.swing.*;
|
||||
@ -59,8 +58,6 @@ public final class Main {
|
||||
|
||||
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
||||
|
||||
VMUtils.patch();
|
||||
|
||||
checkJavaFX();
|
||||
|
||||
// Fix title bar not displaying in GTK systems
|
||||
|
@ -0,0 +1,9 @@
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class JavaFXPatcher {
|
||||
public static void patch(Path... jarPaths) {
|
||||
// Nothing to do with Java 8
|
||||
}
|
||||
}
|
@ -6,13 +6,13 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
@ -34,53 +34,12 @@ import static org.jackhuang.hmcl.util.platform.JavaVersion.CURRENT_JAVA;
|
||||
*/
|
||||
public class SelfDependencyPatcher {
|
||||
private static final Path DEPENDENCIES_DIR_PATH = HMCL_DIRECTORY.resolve("dependencies");
|
||||
private static final Map<Integer, List<String>> JFX_DEPENDENCIES = new LinkedHashMap<Integer, List<String>>(5, 1F) {
|
||||
private static final String DEFAULT_JFX_VERSION = "16";
|
||||
private static final Map<String, String> JFX_DEPENDENCIES_ = new HashMap<String, String>() {
|
||||
{
|
||||
put(15, Arrays.asList(
|
||||
jfxUrl("swing", "15.0.1"),
|
||||
jfxUrl("media", "15.0.1"),
|
||||
jfxUrl("fxml", "15.0.1"),
|
||||
jfxUrl("web", "15.0.1"),
|
||||
jfxUrl("controls", "15.0.1"),
|
||||
jfxUrl("graphics", "15.0.1"),
|
||||
jfxUrl("base", "15.0.1")
|
||||
));
|
||||
put(14, Arrays.asList(
|
||||
jfxUrl("swing", "14.0.2"),
|
||||
jfxUrl("media", "14.0.2"),
|
||||
jfxUrl("fxml", "14.0.2"),
|
||||
jfxUrl("web", "14.0.2"),
|
||||
jfxUrl("controls", "14.0.2"),
|
||||
jfxUrl("graphics", "14.0.2"),
|
||||
jfxUrl("base", "14.0.2")
|
||||
));
|
||||
put(13, Arrays.asList(
|
||||
jfxUrl("swing", "13.0.2"),
|
||||
jfxUrl("media", "13.0.2"),
|
||||
jfxUrl("fxml", "13.0.2"),
|
||||
jfxUrl("web", "13.0.2"),
|
||||
jfxUrl("controls", "13.0.2"),
|
||||
jfxUrl("graphics", "13.0.2"),
|
||||
jfxUrl("base", "13.0.2")
|
||||
));
|
||||
put(12, Arrays.asList(
|
||||
jfxUrl("swing", "12.0.2"),
|
||||
jfxUrl("media", "12.0.2"),
|
||||
jfxUrl("fxml", "12.0.2"),
|
||||
jfxUrl("web", "12.0.2"),
|
||||
jfxUrl("controls", "12.0.2"),
|
||||
jfxUrl("graphics", "12.0.2"),
|
||||
jfxUrl("base", "12.0.2")
|
||||
));
|
||||
put(11, Arrays.asList(
|
||||
jfxUrl("swing", "11.0.2"),
|
||||
jfxUrl("media", "11.0.2"),
|
||||
jfxUrl("fxml", "11.0.2"),
|
||||
jfxUrl("web", "11.0.2"),
|
||||
jfxUrl("controls", "11.0.2"),
|
||||
jfxUrl("graphics", "11.0.2"),
|
||||
jfxUrl("base", "11.0.2")
|
||||
));
|
||||
for (String url : new String [] { jfxUrl("base"), jfxUrl("controls"), jfxUrl("fxml"), jfxUrl("graphics"), jfxUrl("media"), jfxUrl("swing"), jfxUrl("web") }) {
|
||||
put(getFileName(url), url);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -151,32 +110,9 @@ public class SelfDependencyPatcher {
|
||||
*/
|
||||
private static void loadFromCache() 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);
|
||||
}
|
||||
});
|
||||
// Fetch UCP of application's ClassLoader
|
||||
// - ((ClassLoaders.AppClassLoader) ClassLoaders.appClassLoader()).ucp
|
||||
Class<?> clsClassLoaders = Class.forName("jdk.internal.loader.ClassLoaders");
|
||||
Object appClassLoader = clsClassLoaders.getDeclaredMethod("appClassLoader").invoke(null);
|
||||
Class<?> ucpOwner = appClassLoader.getClass();
|
||||
// Field removed in 16, but still exists in parent class "BuiltinClassLoader"
|
||||
if (CURRENT_JAVA.getParsedVersion() >= 16)
|
||||
ucpOwner = ucpOwner.getSuperclass();
|
||||
Field fieldUCP = ucpOwner.getDeclaredField("ucp");
|
||||
fieldUCP.setAccessible(true);
|
||||
Object ucp = fieldUCP.get(appClassLoader);
|
||||
Class<?> clsUCP = ucp.getClass();
|
||||
Method addURL = clsUCP.getDeclaredMethod("addURL", URL.class);
|
||||
addURL.setAccessible(true);
|
||||
// Add each jar.
|
||||
for(URL url : jarUrls)
|
||||
addURL.invoke(ucp, url);
|
||||
List<Path> jarPaths = new ArrayList<>();
|
||||
Files.walk(DEPENDENCIES_DIR_PATH).filter(p -> JFX_DEPENDENCIES_.containsKey(p.getFileName().toString())).forEach(jarPaths::add);
|
||||
JavaFXPatcher.patch(jarPaths.toArray(new Path[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,9 +130,9 @@ public class SelfDependencyPatcher {
|
||||
|
||||
ForkJoinTask<Void> task = ForkJoinPool.commonPool().submit(() -> {
|
||||
// Download each dependency
|
||||
List<String> dependencies = getLatestDependencies();
|
||||
for (int i = 0; i < dependencies.size(); i++) {
|
||||
String dependencyUrlPath = dependencies.get(i);
|
||||
Collection<String> dependencies = JFX_DEPENDENCIES_.values();
|
||||
int i = 0;
|
||||
for (String dependencyUrlPath : dependencies) {
|
||||
URL depURL = new URL(dependencyUrlPath);
|
||||
Path dependencyFilePath = DEPENDENCIES_DIR_PATH.resolve(getFileName(dependencyUrlPath));
|
||||
int finalI = i;
|
||||
@ -204,12 +140,9 @@ public class SelfDependencyPatcher {
|
||||
dialog.setStatus(dependencyUrlPath);
|
||||
dialog.setProgress(finalI, dependencies.size());
|
||||
});
|
||||
String expectedHash = NetworkUtils.doGet(NetworkUtils.toURL(dependencyUrlPath + ".sha1"));
|
||||
Files.copy(depURL.openStream(), dependencyFilePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
String actualHash = Hex.encodeHex(DigestUtils.digest("SHA-1", dependencyFilePath));
|
||||
if (!expectedHash.equalsIgnoreCase(actualHash)) {
|
||||
throw new ChecksumMismatchException("SHA-1", expectedHash, actualHash);
|
||||
}
|
||||
checksum(dependencyFilePath, dependencyUrlPath);
|
||||
i++;
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -224,10 +157,31 @@ public class SelfDependencyPatcher {
|
||||
* @return {@code true} when the dependencies directory has files in it.
|
||||
*/
|
||||
private static boolean hasCachedDependencies() {
|
||||
String[] files = DEPENDENCIES_DIR_PATH.toFile().list();
|
||||
if (files == null)
|
||||
if (!Files.isDirectory(DEPENDENCIES_DIR_PATH))
|
||||
return false;
|
||||
return files.length >= getLatestDependencies().size();
|
||||
|
||||
for (String name : JFX_DEPENDENCIES_.keySet()) {
|
||||
Path dependencyFilePath = DEPENDENCIES_DIR_PATH.resolve(name);
|
||||
if (!Files.exists(dependencyFilePath))
|
||||
return false;
|
||||
|
||||
try {
|
||||
checksum(dependencyFilePath, JFX_DEPENDENCIES_.get(name));
|
||||
} catch (ChecksumMismatchException e) {
|
||||
return false;
|
||||
} catch (IOException ignored) {
|
||||
// Ignore other situations
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void checksum(Path dependencyFilePath, String dependencyUrlPath) throws IOException {
|
||||
String expectedHash = NetworkUtils.doGet(NetworkUtils.toURL(dependencyUrlPath + ".sha1"));
|
||||
String actualHash = Hex.encodeHex(DigestUtils.digest("SHA-1", dependencyFilePath));
|
||||
if (!expectedHash.equalsIgnoreCase(actualHash)) {
|
||||
throw new ChecksumMismatchException("SHA-1", expectedHash, actualHash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -242,6 +196,10 @@ public class SelfDependencyPatcher {
|
||||
* @param component Name of the component.
|
||||
* @return Formed URL for the component.
|
||||
*/
|
||||
private static String jfxUrl(String component) {
|
||||
return jfxUrl(component, DEFAULT_JFX_VERSION);
|
||||
}
|
||||
|
||||
private static String jfxUrl(String component, String version) {
|
||||
// https://repo1.maven.org/maven2/org/openjfx/javafx-%s/%s/javafx-%s-%s-%s.jar
|
||||
return String.format("https://maven.aliyun.com/repository/central/org/openjfx/javafx-%s/%s/javafx-%s-%s-%s.jar",
|
||||
@ -250,31 +208,6 @@ public class SelfDependencyPatcher {
|
||||
// component, version, component, version, getMvnName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Latest JavaFX supported version for.
|
||||
*/
|
||||
private static int getLatestSupportedJfxVersion() {
|
||||
int version = CURRENT_JAVA.getParsedVersion();
|
||||
while (version >= 11) {
|
||||
List<String> dependencies = JFX_DEPENDENCIES.get(version);
|
||||
if (dependencies != null)
|
||||
return version;
|
||||
version--;
|
||||
}
|
||||
throw new AssertionError("Failed to get latest supported JFX version");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JavaFX dependencies list for the current VM version.
|
||||
*/
|
||||
private static List<String> getLatestDependencies() {
|
||||
int version = getLatestSupportedJfxVersion();
|
||||
if (version >= 11) {
|
||||
return JFX_DEPENDENCIES.get(version);
|
||||
}
|
||||
throw new AssertionError("Failed to get latest JFX artifact urls");
|
||||
}
|
||||
|
||||
private static String getMvnName() {
|
||||
switch (OperatingSystem.CURRENT_OS) {
|
||||
case LINUX:
|
||||
|
@ -1,5 +1,6 @@
|
||||
package java.lang.module;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -9,9 +10,9 @@ import java.util.Set;
|
||||
*/
|
||||
public interface ModuleFinder {
|
||||
//CHECKSTYLE:OFF
|
||||
static ModuleFinder ofSystem() {
|
||||
static ModuleFinder of(Path... entries) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
Set<ModuleReference> findAll();
|
||||
//CHECKSTYLE:ON
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package java.lang.module;
|
||||
|
||||
public class ModuleReference {
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.lang.module.ModuleReference;
|
||||
|
||||
/**
|
||||
* Dummy java compatibility class
|
||||
*
|
||||
* @author xxDark
|
||||
*/
|
||||
public class BuiltinClassLoader extends ClassLoader {
|
||||
public void loadModule(ModuleReference mref) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
|
||||
public class JavaFXPatcher {
|
||||
public static void patch(Path... jarPaths) {
|
||||
for (ModuleReference mref : ModuleFinder.of(jarPaths).findAll()) {
|
||||
((BuiltinClassLoader) ClassLoader.getSystemClassLoader()).loadModule(mref);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,270 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user