不再使用反射调用 Java 9~11 中引入的 API (#4114)

This commit is contained in:
Glavo 2025-07-25 17:02:37 +08:00 committed by GitHub
parent 24d3bbf4fe
commit 96f68e1017
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 64 additions and 354 deletions

View File

@ -19,14 +19,15 @@ package org.jackhuang.hmcl;
import org.jackhuang.hmcl.util.FileSaver;
import org.jackhuang.hmcl.ui.AwtUtils;
import org.jackhuang.hmcl.util.ModuleHelper;
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
import org.jackhuang.hmcl.util.SwingUtils;
import org.jackhuang.hmcl.java.JavaRuntime;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.file.Files;
import java.util.concurrent.CancellationException;
@ -140,7 +141,18 @@ public final class EntryPoint {
private static void addEnableNativeAccess() {
if (JavaRuntime.CURRENT_VERSION > 21) {
try {
ModuleHelper.addEnableNativeAccess(Class.forName("javafx.stage.Stage")); // javafx.graphics
// javafx.graphics
Module module = Class.forName("javafx.stage.Stage").getModule();
if (module.isNamed()) {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Module.class, MethodHandles.lookup());
MethodHandle implAddEnableNativeAccess = lookup.findVirtual(Module.class,
"implAddEnableNativeAccess", MethodType.methodType(Module.class));
Module ignored = (Module) implAddEnableNativeAccess.invokeExact(module);
} catch (Throwable e) {
e.printStackTrace(System.err);
}
}
} catch (ClassNotFoundException e) {
LOG.error("Failed to add enable native access for JavaFX", e);
showErrorAndExit(i18n("fatal.javafx.incomplete"));
@ -153,9 +165,9 @@ public final class EntryPoint {
if (JavaRuntime.CURRENT_VERSION == 24 || JavaRuntime.CURRENT_VERSION == 25) {
try {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Method trySetMemoryAccessWarned = clazz.getDeclaredMethod("trySetMemoryAccessWarned");
trySetMemoryAccessWarned.setAccessible(true);
trySetMemoryAccessWarned.invoke(null);
boolean ignored = (boolean) MethodHandles.privateLookupIn(clazz, MethodHandles.lookup())
.findStatic(clazz, "trySetMemoryAccessWarned", MethodType.methodType(boolean.class))
.invokeExact();
} catch (Throwable e) {
LOG.warning("Failed to enable unsafe memory access", e);
}

View File

@ -21,7 +21,6 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.java.JavaRuntime;
import org.jackhuang.hmcl.util.Lazy;
import org.jackhuang.hmcl.util.io.JarUtils;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
@ -29,9 +28,6 @@ import org.jackhuang.hmcl.util.platform.SystemUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -152,46 +148,33 @@ public final class FontManager {
return null;
}
if (JavaRuntime.CURRENT_VERSION >= 9) {
try {
MethodHandle methodHandle = MethodHandles.publicLookup().findStatic(Font.class, "loadFonts",
MethodType.methodType(Font[].class, String.class, double.class));
Font[] fonts = Font.loadFonts(file.toUri().toURL().toExternalForm(), DEFAULT_FONT_SIZE);
if (fonts == null) {
LOG.warning("Failed to load font from " + path);
return null;
} else if (fonts.length == 0) {
LOG.warning("No fonts loaded from " + path);
return null;
}
Font[] fonts = (Font[]) methodHandle.invokeExact(file.toUri().toURL().toExternalForm(), DEFAULT_FONT_SIZE);
if (fonts == null) {
LOG.warning("Failed to load font from " + path);
return null;
} else if (fonts.length == 0) {
LOG.warning("No fonts loaded from " + path);
return null;
}
for (Font font : fonts) {
if (font.getFamily().equalsIgnoreCase(family)) {
return font;
}
}
if (family.indexOf(',') >= 0) {
for (String candidateFamily : family.split(",")) {
for (Font font : fonts) {
if (font.getFamily().equalsIgnoreCase(candidateFamily)) {
return font;
}
}
}
}
LOG.warning(String.format("Family '%s' not found in font file '%s'", family, path));
return fonts[0];
} catch (NoSuchMethodException | IllegalAccessException ignored) {
for (Font font : fonts) {
if (font.getFamily().equalsIgnoreCase(family)) {
return font;
}
}
Font font = Font.loadFont(file.toUri().toURL().toExternalForm(), DEFAULT_FONT_SIZE);
if (font == null)
LOG.warning("Failed to load font from " + path);
return font;
if (family.indexOf(',') >= 0) {
for (String candidateFamily : family.split(",")) {
for (Font font : fonts) {
if (font.getFamily().equalsIgnoreCase(candidateFamily)) {
return font;
}
}
}
}
LOG.warning(String.format("Family '%s' not found in font file '%s'", family, path));
return fonts[0];
} catch (Throwable e) {
LOG.warning("Failed to get default font with fc-match", e);
return null;

View File

@ -328,8 +328,15 @@ public final class FXUtils {
private static final Duration TOOLTIP_SLOW_SHOW_DELAY = Duration.millis(500);
private static final Duration TOOLTIP_SHOW_DURATION = Duration.millis(5000);
public static void installTooltip(Node node, Duration showDelay, Duration showDuration, Duration hideDelay, Tooltip tooltip) {
tooltip.setShowDelay(showDelay);
tooltip.setShowDuration(showDuration);
tooltip.setHideDelay(hideDelay);
Tooltip.install(node, tooltip);
}
public static void installFastTooltip(Node node, Tooltip tooltip) {
runInFX(() -> TooltipInstaller.INSTALLER.installTooltip(node, TOOLTIP_FAST_SHOW_DELAY, TOOLTIP_SHOW_DURATION, Duration.ZERO, tooltip));
runInFX(() -> installTooltip(node, TOOLTIP_FAST_SHOW_DELAY, TOOLTIP_SHOW_DURATION, Duration.ZERO, tooltip));
}
public static void installFastTooltip(Node node, String tooltip) {
@ -337,7 +344,7 @@ public final class FXUtils {
}
public static void installSlowTooltip(Node node, Tooltip tooltip) {
runInFX(() -> TooltipInstaller.INSTALLER.installTooltip(node, TOOLTIP_SLOW_SHOW_DELAY, TOOLTIP_SHOW_DURATION, Duration.ZERO, tooltip));
runInFX(() -> installTooltip(node, TOOLTIP_SLOW_SHOW_DELAY, TOOLTIP_SHOW_DURATION, Duration.ZERO, tooltip));
}
public static void installSlowTooltip(Node node, String tooltip) {

View File

@ -224,7 +224,7 @@ public final class LogWindow extends Stage {
try {
if (gameProcess.isRunning()) {
GameDumpGenerator.writeDumpTo(gameProcess.getPID(), dumpFile);
GameDumpGenerator.writeDumpTo(gameProcess.getProcess().pid(), dumpFile);
FXUtils.showFileInExplorer(dumpFile);
}
} catch (Throwable e) {

View File

@ -1,123 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui;
import javafx.scene.Node;
import javafx.scene.control.Tooltip;
import javafx.util.Duration;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
/**
* @author Glavo
*/
public class TooltipInstaller {
public static final TooltipInstaller INSTALLER;
static {
TooltipInstaller installer = null;
try {
installer = new NewInstaller();
} catch (Throwable e) {
try {
installer = new OldInstaller();
} catch (Throwable e2) {
e2.addSuppressed(e);
LOG.warning("Failed to initialize TooltipInstaller", e2);
}
}
INSTALLER = installer != null ? installer : new TooltipInstaller();
}
TooltipInstaller() {
}
public void installTooltip(Node node, Duration showDelay, Duration showDuration, Duration hideDelay, Tooltip tooltip) {
Tooltip.install(node, tooltip);
}
// For Java 8
private static final class OldInstaller extends TooltipInstaller {
private static final Constructor<?> createTooltipBehavior;
private static final Method installTooltipBehavior;
static {
try {
Class<?> behaviorClass = Class.forName("javafx.scene.control.Tooltip$TooltipBehavior");
createTooltipBehavior = behaviorClass.getDeclaredConstructor(Duration.class, Duration.class, Duration.class, boolean.class);
createTooltipBehavior.setAccessible(true);
installTooltipBehavior = behaviorClass.getDeclaredMethod("install", Node.class, Tooltip.class);
installTooltipBehavior.setAccessible(true);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
@Override
public void installTooltip(Node node, Duration showDelay, Duration showDuration, Duration hideDelay, Tooltip tooltip) {
try {
Object behavior = createTooltipBehavior.newInstance(showDelay, showDuration, hideDelay, false);
installTooltipBehavior.invoke(behavior, node, tooltip);
} catch (ReflectiveOperationException e) {
LOG.warning("Failed to set tooltip show delay", e);
Tooltip.install(node, tooltip);
}
}
}
// For Java 9+
private static final class NewInstaller extends TooltipInstaller {
private static final MethodHandle setTooltipShowDelay;
private static final MethodHandle setTooltipShowDuration;
private static final MethodHandle setTooltipHideDelay;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodType methodType = MethodType.methodType(void.class, Duration.class);
setTooltipShowDelay = lookup.findVirtual(Tooltip.class, "setShowDelay", methodType);
setTooltipShowDuration = lookup.findVirtual(Tooltip.class, "setShowDuration", methodType);
setTooltipHideDelay = lookup.findVirtual(Tooltip.class, "setHideDelay", methodType);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
@Override
public void installTooltip(Node node, Duration showDelay, Duration showDuration, Duration hideDelay, Tooltip tooltip) {
try {
setTooltipShowDelay.invokeExact(tooltip, showDelay);
setTooltipShowDuration.invokeExact(tooltip, showDuration);
setTooltipHideDelay.invokeExact(tooltip, hideDelay);
} catch (Throwable e) {
LOG.warning("Failed to set tooltip show delay", e);
}
Tooltip.install(node, tooltip);
}
}
}

View File

@ -43,7 +43,6 @@ import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
import org.jackhuang.hmcl.ui.export.ExportWizardProvider;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.File;
@ -110,10 +109,8 @@ public final class Versions {
public static void deleteVersion(Profile profile, String version) {
boolean isIndependent = profile.getVersionSetting(version).getGameDirType() == GameDirectoryType.VERSION_FOLDER;
boolean isMovingToTrashSupported = FileUtils.isMovingToTrashSupported();
String message = isIndependent ? i18n("version.manage.remove.confirm.independent", version) :
isMovingToTrashSupported ? i18n("version.manage.remove.confirm.trash", version, version + "_removed") :
i18n("version.manage.remove.confirm", version);
i18n("version.manage.remove.confirm.trash", version, version + "_removed");
JFXButton deleteButton = new JFXButton(i18n("button.delete"));
deleteButton.getStyleClass().add("dialog-error");

View File

@ -1,46 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
/**
* @author Glavo
*/
public final class ModuleHelper {
public static void addEnableNativeAccess(Class<?> clazzInModule) {
Module module = clazzInModule.getModule();
if (module.isNamed()) {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Module.class, MethodHandles.lookup());
MethodHandle implAddEnableNativeAccess = lookup.findVirtual(Module.class, "implAddEnableNativeAccess", MethodType.methodType(Module.class));
Module ignored = (Module) implAddEnableNativeAccess.invokeExact(module);
} catch (Throwable e) {
e.printStackTrace(System.err);
}
} else {
System.err.println("TODO: Add enable native access for anonymous modules is not yet supported");
}
}
private ModuleHelper() {
}
}

View File

@ -1433,7 +1433,6 @@ version.manage.manage=Edit Instance
version.manage.manage.title=Edit Instance - %1s
version.manage.redownload_assets_index=Update Game Assets
version.manage.remove=Delete Instance
version.manage.remove.confirm=Are you sure you want to permanently remove the instance "%s"? This operation cannot be undone!!!
version.manage.remove.confirm.trash=Are you sure you want to remove the instance "%s"? You can still find its files in your recycle bin by the name of "%s".
version.manage.remove.confirm.independent=Since this instance is stored in an isolated directory, deleting it will also delete its saves and other data. Do you still want to delete the instance "%s"?
version.manage.remove_assets=Delete All Assets

View File

@ -1406,7 +1406,6 @@ version.manage.manage=Editar instancia
version.manage.manage.title=Editar instancia - %1s
version.manage.redownload_assets_index=Actualizar activos del juego
version.manage.remove=Eliminar instancia
version.manage.remove.confirm=¿Está seguro de que quiere eliminar permanentemente la instancia «%s»? ¡Esta operación no se puede deshacer!
version.manage.remove.confirm.trash=¿Estás seguro de que quieres eliminar la instancia «%s»? Todavía puedes encontrar sus archivos en tu papelera de reciclaje con el nombre de «%s».
version.manage.remove.confirm.independent=Dado que esta instancia está almacenada en un directorio aislado, al eliminarla también se eliminarán sus guardados y otros datos. ¿Aún quieres borrar la instancia %s?
version.manage.remove_assets=Borrar todas las activos del juego

View File

@ -949,7 +949,6 @@ version.manage.manage=バージョンの管理
version.manage.manage.title=バージョンの管理 - %1s
version.manage.redownload_assets_index=ゲームアセットファイルの更新
version.manage.remove=このバージョンを削除します
version.manage.remove.confirm=このバージョン %s を削除してもよろしいですか?このバージョンを再度復元することはできません。
version.manage.remove.confirm.trash=このバージョン %s を削除してもよろしいですか?このバージョンは、システムのゴミ箱に %s という名前で復元できます。
version.manage.remove.confirm.independent=このバージョンは独立モードであるため、このバージョンを削除すると、このバージョンに属するすべての保存済みワールドも削除されます。このバージョン %s を削除しますか?
version.manage.remove_assets=すべてのゲームリソースファイルの削除

View File

@ -1406,7 +1406,6 @@ version.manage.manage=Изменить сборку
version.manage.manage.title=Изменить сборку - %1s
version.manage.redownload_assets_index=Обновить файлы игровых активов
version.manage.remove=Удалить
version.manage.remove.confirm=Удалить %s? Вы не сможете восстановить эту сборку снова!
version.manage.remove.confirm.trash=Удалить %s? Вы можете восстановить эту сборку с именем %s из корзины системы.
version.manage.remove.confirm.independent=Поскольку эта сборка находится в режиме разделения, удаление этой сборки приведет к удалению всех миров, принадлежащих этой сборке. Удалить %s?
version.manage.remove_assets=Удалить файлы игровых активов

View File

@ -1226,7 +1226,6 @@ version.manage.manage=實例管理
version.manage.manage.title=實例管理 - %1s
version.manage.redownload_assets_index=更新遊戲資源檔案
version.manage.remove=刪除該實例
version.manage.remove.confirm=真的要刪除實例「%s」嗎? 你將無法找回被刪除的檔案!!!
version.manage.remove.confirm.trash=真的要刪除實例「%s」嗎? 你可以在系統的資源回收筒 (或垃圾桶) 中還原目錄「%s」來找回該實例。
version.manage.remove.confirm.independent=由於該實例啟用了「(全域/實例特定) 遊戲設定 → 執行路徑 → 各實例獨立」設定,刪除該實例將導致該遊戲的存檔等資料一同被刪除!真的要刪除實例「%s」嗎?
version.manage.remove_assets=刪除所有遊戲資源檔案

View File

@ -1236,7 +1236,6 @@ version.manage.manage=版本管理
version.manage.manage.title=版本管理 - %1s
version.manage.redownload_assets_index=更新游戏资源文件
version.manage.remove=删除该版本
version.manage.remove.confirm=真的要删除版本“%s”吗你将无法找回被删除的文件
version.manage.remove.confirm.trash=真的要删除版本“%s”吗你可以在系统的回收站中还原“%s”文件夹来找回该版本。
version.manage.remove.confirm.independent=由于该游戏启用了“(全局/版本特定) 游戏设置 → 版本隔离 → 各版本独立”选项,删除该版本将导致该游戏的存档等数据一同被删除!真的要删除版本“%s”吗
version.manage.remove_assets=删除所有游戏资源文件

View File

@ -17,10 +17,6 @@
*/
package org.jackhuang.hmcl;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public final class JavaFXLauncher {
private JavaFXLauncher() {
@ -31,21 +27,9 @@ public final class JavaFXLauncher {
static {
// init JavaFX Toolkit
try {
// Java 9 or Latter
final MethodHandle startup =
MethodHandles.publicLookup().findStatic(
javafx.application.Platform.class, "startup", MethodType.methodType(void.class, Runnable.class));
startup.invokeExact((Runnable) () -> {
javafx.application.Platform.startup(() -> {
});
started = true;
} catch (NoSuchMethodException e) {
// Java 8
try {
Class.forName("javafx.embed.swing.JFXPanel").getDeclaredConstructor().newInstance();
started = true;
} catch (Throwable e0) {
e0.printStackTrace();
}
} catch (IllegalStateException e) {
started = true;
} catch (Throwable e) {

View File

@ -246,7 +246,7 @@ public class DefaultGameRepository implements GameRepository {
try {
versions.remove(id);
if (FileUtils.isMovingToTrashSupported() && FileUtils.moveToTrash(removedFile)) {
if (FileUtils.moveToTrash(removedFile)) {
return true;
}

View File

@ -45,7 +45,7 @@ public final class StringUtils {
}
public static boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
return str == null || str.isBlank();
}
public static boolean isNotBlank(String str) {

View File

@ -24,14 +24,12 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.File;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.*;
import java.nio.file.*;
import java.nio.file.spi.FileSystemProvider;
import java.util.*;
import java.util.zip.ZipError;
import java.util.zip.ZipException;
/**
@ -59,7 +57,7 @@ public final class CompressingUtils {
}
}
public static boolean testEncoding(ZipArchiveReader zipFile, Charset encoding) throws IOException {
public static boolean testEncoding(ZipArchiveReader zipFile, Charset encoding) {
CharsetDecoder cd = newCharsetDecoder(encoding);
CharBuffer cb = CharBuffer.allocate(32);
@ -71,7 +69,7 @@ public final class CompressingUtils {
int clen = (int)(ba.length * cd.maxCharsPerByte());
if (clen == 0) continue;
if (clen <= cb.capacity())
((Buffer) cb).clear(); // cast to prevent "java.lang.NoSuchMethodError: java.nio.CharBuffer.clear()Ljava/nio/CharBuffer;" when compiling with Java 9+
cb.clear();
else
cb = CharBuffer.allocate(clen);
@ -212,9 +210,6 @@ public final class CompressingUtils {
throw new FileSystemNotFoundException("Module jdk.zipfs does not exist");
return ZIPFS_PROVIDER.newFileSystem(zipFile, env);
} catch (ZipError error) {
// Since Java 8 throws ZipError stupidly
throw new ZipException(error.getMessage());
} catch (UnsupportedOperationException ex) {
throw new ZipException("Not a zip file");
} catch (FileSystemNotFoundException ex) {

View File

@ -25,7 +25,6 @@ import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.*;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
@ -328,18 +327,9 @@ public final class FileUtils {
/**
* Move file to trash.
* <p>
* This method is only implemented in Java 9. Please check we are using Java 9 by invoking isMovingToTrashSupported.
* Example:
* <pre>{@code
* if (FileUtils.isMovingToTrashSupported()) {
* FileUtils.moveToTrash(file);
* }
* }</pre>
*
* @param file the file being moved to trash.
* @return false if moveToTrash does not exist, or platform does not support Desktop.Action.MOVE_TO_TRASH
* @see FileUtils#isMovingToTrashSupported()
*/
public static boolean moveToTrash(File file) {
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && hasKnownDesktop()) {
@ -393,33 +383,12 @@ public final class FileUtils {
}
try {
java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
Method moveToTrash = desktop.getClass().getMethod("moveToTrash", File.class);
moveToTrash.invoke(desktop, file);
return true;
return java.awt.Desktop.getDesktop().moveToTrash(file);
} catch (Exception e) {
return false;
}
}
/**
* Check if {@code java.awt.Desktop.moveToTrash} exists.
*
* @return true if the method exists.
*/
public static boolean isMovingToTrashSupported() {
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && hasKnownDesktop()) {
return true;
}
try {
java.awt.Desktop.class.getMethod("moveToTrash", File.class);
return true;
} catch (ReflectiveOperationException e) {
return false;
}
}
public static void cleanDirectory(File directory)
throws IOException {
if (!directory.exists()) {

View File

@ -78,7 +78,7 @@ public final class IOUtils {
public static String readFullyAsStringWithClosing(InputStream stream) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream(Math.max(stream.available(), 32));
copyTo(stream, result);
return result.toString("UTF-8");
return result.toString(UTF_8);
}
/**
@ -101,11 +101,11 @@ public final class IOUtils {
}
public static String readFullyAsString(InputStream stream) throws IOException {
return readFully(stream).toString("UTF-8");
return readFully(stream).toString(UTF_8);
}
public static String readFullyAsString(InputStream stream, Charset charset) throws IOException {
return readFully(stream).toString(charset.name());
return readFully(stream).toString(charset);
}
public static void copyTo(InputStream src, OutputStream dest) throws IOException {

View File

@ -294,7 +294,7 @@ public final class Logger {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
exportLogs(output);
return output.toString("UTF-8");
return output.toString(UTF_8);
} catch (IOException e) {
log(Level.WARNING, CLASS_NAME + ".getLogs", "Failed to export logs", e);
return "";

View File

@ -17,14 +17,10 @@
*/
package org.jackhuang.hmcl.util.platform;
import org.jackhuang.hmcl.java.JavaRuntime;
import org.jackhuang.hmcl.launch.StreamPump;
import org.jackhuang.hmcl.util.Lang;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
@ -84,47 +80,6 @@ public final class ManagedProcess {
return process;
}
/**
* The PID of the raw system process
*
* @throws UnsupportedOperationException if current Java environment is not supported.
* @return PID
*/
public long getPID() throws UnsupportedOperationException {
if (JavaRuntime.CURRENT_VERSION >= 9) {
// Method Process.pid() is provided (Java 9 or later). Invoke it to get the pid.
try {
return (long) MethodHandles.publicLookup()
.findVirtual(Process.class, "pid", MethodType.methodType(long.class))
.invokeExact(process);
} catch (Throwable e) {
throw new UnsupportedOperationException("Cannot get the pid", e);
}
} else {
// Method Process.pid() is not provided. (Java 8).
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
// On Windows, we can invoke method Process.pid() to get the pid.
// However, this method is supplied since Java 9.
// So, there is no ways to get the pid.
throw new UnsupportedOperationException("Cannot get the pid of a Process on Java 8 on Windows.");
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS || OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
// On Linux or Mac, we can get field UnixProcess.pid field to get the pid.
// All the Java version is accepted.
// See https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/solaris/classes/java/lang/UNIXProcess.java.linux
try {
Field pidField = process.getClass().getDeclaredField("pid");
pidField.setAccessible(true);
return pidField.getInt(process);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new UnsupportedOperationException("Cannot get the pid of a Process on Java 8 on macOS/Linux.", e);
}
} else {
// Unknown Operating System, no fallback available.
throw new UnsupportedOperationException(String.format("Cannot get the pid of a Process on Java 8 on Unknown Operating System (%s).", System.getProperty("os.name")));
}
}
}
/**
* The command line.
*

View File

@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.util.platform;
import org.jackhuang.hmcl.java.JavaRuntime;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
@ -110,7 +109,7 @@ public final class SystemUtils {
}
public static boolean supportJVMAttachment() {
return JavaRuntime.CURRENT_VERSION >= 9 && Thread.currentThread().getContextClassLoader().getResource("com/sun/tools/attach/VirtualMachine.class") != null;
return Thread.currentThread().getContextClassLoader().getResource("com/sun/tools/attach/VirtualMachine.class") != null;
}
private static void onLogLine(String log) {

View File

@ -17,10 +17,6 @@
*/
package org.jackhuang.hmcl;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public final class JavaFXLauncher {
private JavaFXLauncher() {
@ -31,21 +27,9 @@ public final class JavaFXLauncher {
static {
// init JavaFX Toolkit
try {
// Java 9 or Latter
final MethodHandle startup =
MethodHandles.publicLookup().findStatic(
javafx.application.Platform.class, "startup", MethodType.methodType(void.class, Runnable.class));
startup.invokeExact((Runnable) () -> {
javafx.application.Platform.startup(() -> {
});
started = true;
} catch (NoSuchMethodException e) {
// Java 8
try {
Class.forName("javafx.embed.swing.JFXPanel").getDeclaredConstructor().newInstance();
started = true;
} catch (Throwable e0) {
e0.printStackTrace();
}
} catch (IllegalStateException e) {
started = true;
} catch (Throwable e) {