From c323292e148fd128139096334d2351a7085b3c96 Mon Sep 17 00:00:00 2001 From: PerrierBouteille Date: Thu, 18 Jun 2026 11:53:27 +0200 Subject: [PATCH 1/5] Java 25: patch arclight-api Unsafe for runtime compat sun.misc.Unsafe.ensureClassInitialized / shouldBeInitialized were removed in JDK 22+, and SecurityManager's constructor throws on JDK 24+ (JEP 486), so io.izzel.arclight.api.Unsafe crashed at class-init on Java 25. Forked arclight-api-1.5.4: route those two methods to jdk.internal.misc.Unsafe via the trusted IMPL_LOOKUP, force the StackWalker caller-class path, and neutralise the now-dead defineAnonymousClass branch. The patched jar is embedded in place of the Maven artifact. Verified: the server still boots on JDK 17 and now gets past ForgeInstaller + Forge launcher bootstrap on JDK 25. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 6 ++++++ arclight-forge/build.gradle | 4 +++- arclight-forge/libs/arclight-api-1.5.4-j25.jar | Bin 0 -> 15025 bytes 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 arclight-forge/libs/arclight-api-1.5.4-j25.jar diff --git a/.gitignore b/.gitignore index 7bde298fa..7f140ee59 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ build/ /.settings/ .idea *.log + +# Java 25 migration: local test run dirs and IDE module files +run_j17_test/ +run_j25_test/ +run_prod/ +*.iml diff --git a/arclight-forge/build.gradle b/arclight-forge/build.gradle index c6ea85cd4..1095af5a1 100644 --- a/arclight-forge/build.gradle +++ b/arclight-forge/build.gradle @@ -80,7 +80,9 @@ dependencies { } embed 'io.izzel.arclight:mixin-tools:1.0.1' embed "io.izzel:tools:$toolsVersion" - embed "io.izzel.arclight:arclight-api:$apiVersion" + // Java 25: patched arclight-api (Unsafe routed to jdk.internal.misc.Unsafe, StackWalker + // caller-class instead of SecurityManager). See ~/.arclight-proxy/arclight-api-fork. + embed files("libs/arclight-api-${apiVersion}-j25.jar") embed 'commons-lang:commons-lang:2.6@jar' embed(project(':i18n-config')) { transitive = false diff --git a/arclight-forge/libs/arclight-api-1.5.4-j25.jar b/arclight-forge/libs/arclight-api-1.5.4-j25.jar new file mode 100644 index 0000000000000000000000000000000000000000..7ff2f7911eac5bfe4d5a505c0a1f15ebbdaa2f06 GIT binary patch literal 15025 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 literal 0 HcmV?d00001 From a5b0f5d554de0a12c98fb2c06c4115a7f90c1248 Mon Sep 17 00:00:00 2001 From: PerrierBouteille Date: Thu, 18 Jun 2026 12:45:33 +0200 Subject: [PATCH 2/5] Java 25: move ASM event-executor gen out of JavaPluginLoaderMixin On Java 25 the Mixin (0.8.5) bytecode provider cannot resolve org.objectweb.asm.ClassVisitor metadata, so preprocessing JavaPluginLoaderMixin (whose merged methods referenced ClassVisitor/ClassWriter) crashed with ClassMetadataNotFoundException, aborting server start. Extract the ASM EventExecutor generation (createExecutor/createConstructor/ createImpl + the cache/counter) into a normal helper class EventExecutorGenerator that the mixin calls. The mixin no longer exposes any org.objectweb.asm.* type to Mixin's preprocessor; the ASM classes still load and run fine at runtime. With this + the Unsafe patch the server now reaches "Done! For help, type help" on Java 25 (verified on Corretto 25), and still on 17. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../mixin/bukkit/JavaPluginLoaderMixin.java | 131 +-------------- .../mod/util/EventExecutorGenerator.java | 151 ++++++++++++++++++ 2 files changed, 153 insertions(+), 129 deletions(-) create mode 100644 arclight-common/src/main/java/io/izzel/arclight/common/mod/util/EventExecutorGenerator.java diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/JavaPluginLoaderMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/JavaPluginLoaderMixin.java index 9a539d68a..bc9c71774 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/JavaPluginLoaderMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/JavaPluginLoaderMixin.java @@ -1,11 +1,9 @@ package io.izzel.arclight.common.mixin.bukkit; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import io.izzel.arclight.api.Unsafe; import io.izzel.arclight.common.bridge.bukkit.JavaPluginLoaderBridge; import io.izzel.arclight.common.bridge.bukkit.PluginClassLoaderBridge; import io.izzel.arclight.common.mod.server.ArclightServer; +import io.izzel.arclight.common.mod.util.EventExecutorGenerator; import io.izzel.arclight.i18n.ArclightConfig; import org.apache.commons.lang3.Validate; import org.bukkit.Server; @@ -16,24 +14,14 @@ import org.bukkit.plugin.*; import org.bukkit.plugin.java.JavaPluginLoader; import org.jetbrains.annotations.NotNull; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.net.URLClassLoader; import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @Mixin(value = JavaPluginLoader.class, remap = false) @@ -46,15 +34,6 @@ public abstract class JavaPluginLoaderMixin implements JavaPluginLoaderBridge { @Accessor("loaders") public abstract List arclight$getLoaders(); // @formatter:on - private static final AtomicInteger COUNTER = new AtomicInteger(); - private static final Cache> EXECUTOR_CACHE = CacheBuilder.newBuilder() - .expireAfterAccess(1, TimeUnit.HOURS) - .build(); - private static final String HIDDEN_FORM = - Float.parseFloat(System.getProperty("java.class.version")) < 57 - ? "Ljava/lang/invoke/LambdaForm$Hidden;" - : "Ljdk/internal/vm/annotation/Hidden;"; - /** * @author InitAuther97 * @reason Support plugin class loader isolation @@ -156,7 +135,7 @@ public Map, Set> createRegisteredList // final CustomTimingsHandler timings = new CustomTimingsHandler("Plugin: " + plugin.getDescription().getFullName() + " Event: " + listener.getClass().getName() + "::" + method.getName() + "(" + eventClass.getSimpleName() + ")", pluginParentTimer); // Spigot try { - Class executorClass = createExecutor(method, eventClass); + Class executorClass = EventExecutorGenerator.createExecutor(method, eventClass); Constructor constructor = executorClass.getDeclaredConstructor(); constructor.setAccessible(true); EventExecutor executor = constructor.newInstance(); @@ -168,110 +147,4 @@ public Map, Set> createRegisteredList return ret; } - @SuppressWarnings("unchecked") - private Class createExecutor(Method method, Class eventClass) throws ExecutionException { - return EXECUTOR_CACHE.get(method, () -> { - ClassWriter cv = new ClassWriter(ClassWriter.COMPUTE_MAXS); - cv.visit(Opcodes.V1_8, - Opcodes.ACC_SUPER + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_FINAL, - Type.getInternalName(method.getDeclaringClass()) + "$$arclight$" + COUNTER.getAndIncrement(), - null, - Type.getInternalName(Object.class), - new String[]{Type.getInternalName(EventExecutor.class)} - ); - cv.visitOuterClass(Type.getInternalName(method.getDeclaringClass()), null, null); - createConstructor(cv); - createImpl(method, eventClass, cv); - cv.visitEnd(); - return (Class) Unsafe.defineAnonymousClass(method.getDeclaringClass(), cv.toByteArray(), null); - }); - } - - private void createConstructor(ClassVisitor cv) { - MethodVisitor mv = cv.visitMethod( - Opcodes.ACC_PRIVATE, - "", - "()V", - null, null); - mv.visitCode(); - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(-1, -1); - mv.visitEnd(); - } - - private void createImpl(Method method, Class eventClass, ClassVisitor cv) { - String ownerType = Type.getInternalName(method.getDeclaringClass()); - MethodVisitor mv = cv.visitMethod( - Opcodes.ACC_PUBLIC, - "execute", - Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Listener.class), Type.getType(Event.class)), - null, null - ); - mv.visitAnnotation(HIDDEN_FORM, true); - - Label label0 = new Label(); - Label label1 = new Label(); - Label label2 = new Label(); - mv.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable"); - Label label3 = new Label(); - Label label4 = new Label(); - // try { - mv.visitTryCatchBlock(label3, label4, label2, "java/lang/Throwable"); - // if (!(event instanceof TYPE)) - mv.visitLabel(label0); - mv.visitVarInsn(Opcodes.ALOAD, 2); - mv.visitTypeInsn(Opcodes.INSTANCEOF, Type.getInternalName(eventClass)); - mv.visitJumpInsn(Opcodes.IFNE, label3); - // return; - mv.visitLabel(label1); - mv.visitInsn(Opcodes.RETURN); - mv.visitLabel(label3); - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - // ((TYPE) listener).(event); - // TYPE.(event); - int invokeCode; - if (Modifier.isStatic(method.getModifiers())) { - invokeCode = Opcodes.INVOKESTATIC; - } else if (method.getDeclaringClass().isInterface()) { - invokeCode = Opcodes.INVOKEINTERFACE; - } else { - invokeCode = Opcodes.INVOKEVIRTUAL; - } - if (invokeCode != Opcodes.INVOKESTATIC) { - mv.visitVarInsn(Opcodes.ALOAD, 1); - mv.visitTypeInsn(Opcodes.CHECKCAST, ownerType); - } - mv.visitVarInsn(Opcodes.ALOAD, 2); - mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(eventClass)); - mv.visitMethodInsn(invokeCode, ownerType, method.getName(), Type.getMethodDescriptor(method), invokeCode == Opcodes.INVOKEINTERFACE); - int retSize = Type.getType(method.getReturnType()).getSize(); - if (retSize > 0) { - mv.visitInsn(Opcodes.POP + retSize - 1); - } - mv.visitLabel(label4); - // } catch (Throwable t) { - Label label5 = new Label(); - mv.visitJumpInsn(Opcodes.GOTO, label5); - mv.visitLabel(label2); - mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"}); - mv.visitVarInsn(Opcodes.ASTORE, 3); - // throw new EventException(t); - Label label6 = new Label(); - mv.visitLabel(label6); - mv.visitTypeInsn(Opcodes.NEW, "org/bukkit/event/EventException"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.ALOAD, 3); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "org/bukkit/event/EventException", "", "(Ljava/lang/Throwable;)V", false); - mv.visitInsn(Opcodes.ATHROW); - mv.visitLabel(label5); - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - mv.visitInsn(Opcodes.RETURN); - // } - Label label7 = new Label(); - mv.visitLabel(label7); - mv.visitMaxs(-1, -1); - mv.visitEnd(); - } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/EventExecutorGenerator.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/EventExecutorGenerator.java new file mode 100644 index 000000000..dff709f5f --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/EventExecutorGenerator.java @@ -0,0 +1,151 @@ +package io.izzel.arclight.common.mod.util; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import io.izzel.arclight.api.Unsafe; +import org.bukkit.event.Event; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * ASM generation of plugin {@link EventExecutor}s, extracted out of + * {@code JavaPluginLoaderMixin} so that Mixin never has to resolve the + * org.objectweb.asm.* types referenced here. On Java 25 the Mixin bytecode + * provider cannot load org.objectweb.asm.ClassVisitor metadata, which crashed + * the preprocessor when these methods lived inside the mixin. Keeping them in a + * normal class avoids that resolution entirely; the ASM classes still load and + * run fine at runtime. + */ +public final class EventExecutorGenerator { + + private EventExecutorGenerator() { + } + + private static final AtomicInteger COUNTER = new AtomicInteger(); + private static final Cache> EXECUTOR_CACHE = CacheBuilder.newBuilder() + .expireAfterAccess(1, TimeUnit.HOURS) + .build(); + private static final String HIDDEN_FORM = + Float.parseFloat(System.getProperty("java.class.version")) < 57 + ? "Ljava/lang/invoke/LambdaForm$Hidden;" + : "Ljdk/internal/vm/annotation/Hidden;"; + + @SuppressWarnings("unchecked") + public static Class createExecutor(Method method, Class eventClass) throws ExecutionException { + return EXECUTOR_CACHE.get(method, () -> { + ClassWriter cv = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cv.visit(Opcodes.V1_8, + Opcodes.ACC_SUPER + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_FINAL, + Type.getInternalName(method.getDeclaringClass()) + "$$arclight$" + COUNTER.getAndIncrement(), + null, + Type.getInternalName(Object.class), + new String[]{Type.getInternalName(EventExecutor.class)} + ); + cv.visitOuterClass(Type.getInternalName(method.getDeclaringClass()), null, null); + createConstructor(cv); + createImpl(method, eventClass, cv); + cv.visitEnd(); + return (Class) Unsafe.defineAnonymousClass(method.getDeclaringClass(), cv.toByteArray(), null); + }); + } + + private static void createConstructor(ClassVisitor cv) { + MethodVisitor mv = cv.visitMethod( + Opcodes.ACC_PRIVATE, + "", + "()V", + null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + + private static void createImpl(Method method, Class eventClass, ClassVisitor cv) { + String ownerType = Type.getInternalName(method.getDeclaringClass()); + MethodVisitor mv = cv.visitMethod( + Opcodes.ACC_PUBLIC, + "execute", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Listener.class), Type.getType(Event.class)), + null, null + ); + mv.visitAnnotation(HIDDEN_FORM, true); + + Label label0 = new Label(); + Label label1 = new Label(); + Label label2 = new Label(); + mv.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable"); + Label label3 = new Label(); + Label label4 = new Label(); + // try { + mv.visitTryCatchBlock(label3, label4, label2, "java/lang/Throwable"); + // if (!(event instanceof TYPE)) + mv.visitLabel(label0); + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitTypeInsn(Opcodes.INSTANCEOF, Type.getInternalName(eventClass)); + mv.visitJumpInsn(Opcodes.IFNE, label3); + // return; + mv.visitLabel(label1); + mv.visitInsn(Opcodes.RETURN); + mv.visitLabel(label3); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + // ((TYPE) listener).(event); + // TYPE.(event); + int invokeCode; + if (Modifier.isStatic(method.getModifiers())) { + invokeCode = Opcodes.INVOKESTATIC; + } else if (method.getDeclaringClass().isInterface()) { + invokeCode = Opcodes.INVOKEINTERFACE; + } else { + invokeCode = Opcodes.INVOKEVIRTUAL; + } + if (invokeCode != Opcodes.INVOKESTATIC) { + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, ownerType); + } + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(eventClass)); + mv.visitMethodInsn(invokeCode, ownerType, method.getName(), Type.getMethodDescriptor(method), invokeCode == Opcodes.INVOKEINTERFACE); + int retSize = Type.getType(method.getReturnType()).getSize(); + if (retSize > 0) { + mv.visitInsn(Opcodes.POP + retSize - 1); + } + mv.visitLabel(label4); + // } catch (Throwable t) { + Label label5 = new Label(); + mv.visitJumpInsn(Opcodes.GOTO, label5); + mv.visitLabel(label2); + mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"}); + mv.visitVarInsn(Opcodes.ASTORE, 3); + // throw new EventException(t); + Label label6 = new Label(); + mv.visitLabel(label6); + mv.visitTypeInsn(Opcodes.NEW, "org/bukkit/event/EventException"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 3); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "org/bukkit/event/EventException", "", "(Ljava/lang/Throwable;)V", false); + mv.visitInsn(Opcodes.ATHROW); + mv.visitLabel(label5); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitInsn(Opcodes.RETURN); + // } + Label label7 = new Label(); + mv.visitLabel(label7); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } +} From ba4673e289862e7770c68ab67189a1379e886168 Mon Sep 17 00:00:00 2001 From: PerrierBouteille Date: Thu, 18 Jun 2026 12:52:18 +0200 Subject: [PATCH 3/5] Java 25: fix URLClassPath constructor for classpath injection ForgeInstaller.addToPath built jdk.internal.loader.URLClassPath via its (URL[], AccessControlContext) constructor, which was removed on JDK 24+ when JEP 486 dropped the AccessControlContext parameter. On Java 25 this threw NoSuchMethodException on every addToPath call (spammed to stderr). Try the legacy (URL[], AccessControlContext) constructor first (JDK 17-21) and fall back to the new (URL[]) constructor (JDK 24+). Verified: no more spam and the server reaches "Done! For help, type help" on Corretto 25. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../izzel/arclight/forgeinstaller/ForgeInstaller.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/forge-installer/src/main/java/io/izzel/arclight/forgeinstaller/ForgeInstaller.java b/forge-installer/src/main/java/io/izzel/arclight/forgeinstaller/ForgeInstaller.java index 45b56f3dd..e9defc5f3 100644 --- a/forge-installer/src/main/java/io/izzel/arclight/forgeinstaller/ForgeInstaller.java +++ b/forge-installer/src/main/java/io/izzel/arclight/forgeinstaller/ForgeInstaller.java @@ -410,8 +410,15 @@ public static void addToPath(Path path) { Object ucp = Unsafe.getObject(loader, offset); if (ucp == null) { var cl = Class.forName("jdk.internal.loader.URLClassPath"); - var handle = Unsafe.lookup().findConstructor(cl, MethodType.methodType(void.class, URL[].class, AccessControlContext.class)); - ucp = handle.invoke(new URL[]{}, (AccessControlContext) null); + try { + // JDK 17-21: URLClassPath(URL[], AccessControlContext) + var handle = Unsafe.lookup().findConstructor(cl, MethodType.methodType(void.class, URL[].class, AccessControlContext.class)); + ucp = handle.invoke(new URL[]{}, (AccessControlContext) null); + } catch (NoSuchMethodException e) { + // JDK 24+ (JEP 486) dropped the AccessControlContext parameter + var handle = Unsafe.lookup().findConstructor(cl, MethodType.methodType(void.class, URL[].class)); + ucp = handle.invoke((Object) new URL[]{}); + } Unsafe.putObjectVolatile(loader, offset, ucp); } Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class); From 9068d18669995550b396a00c83447b6affcf6e54 Mon Sep 17 00:00:00 2001 From: PerrierBouteille Date: Thu, 18 Jun 2026 13:13:34 +0200 Subject: [PATCH 4/5] Java 25: fix defineAnonymousClass invokeExact arg type in patched arclight-api Regression from rebuilding arclight-api's decompiled Unsafe: the decompiler had dropped the (Object) cast on the last invokeExact argument of defineAnonymousClass, so the recompiled call passed `objects` as Object[] while the H_DEF_CLASS handle (ClassLoader.defineClass0) expects Object, throwing WrongMethodTypeException whenever a plugin registered event listeners (JavaPluginLoader.createRegisteredListeners -> EventExecutorGenerator). Restore the (Object) cast. Verified with a test plugin on Corretto 25: listener registration generates the EventExecutor and ServerLoadEvent is delivered to the plugin. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../libs/arclight-api-1.5.4-j25.jar | Bin 15025 -> 15023 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/arclight-forge/libs/arclight-api-1.5.4-j25.jar b/arclight-forge/libs/arclight-api-1.5.4-j25.jar index 7ff2f7911eac5bfe4d5a505c0a1f15ebbdaa2f06..21b2f0bbfc48d8693d166de27f5158e17081b63c 100644 GIT binary patch delta 4840 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 OlVmza2COvz0002XA|u!U delta 4841 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 Date: Thu, 18 Jun 2026 13:29:36 +0200 Subject: [PATCH 5/5] Java 25: vendor patched arclight-api Unsafe source for transparency The Java 25 fixes for io.izzel.arclight.api.Unsafe ship as a rebuilt binary jar (arclight-forge/libs/arclight-api-1.5.4-j25.jar) because arclight-api's source isn't published. Keep the patched source under arclight-forge/patches/ so the change is reviewable and the jar reproducible. No build behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) --- arclight-forge/patches/README.md | 25 + .../io/izzel/arclight/api/Unsafe.java | 522 ++++++++++++++++++ 2 files changed, 547 insertions(+) create mode 100644 arclight-forge/patches/README.md create mode 100644 arclight-forge/patches/arclight-api/io/izzel/arclight/api/Unsafe.java diff --git a/arclight-forge/patches/README.md b/arclight-forge/patches/README.md new file mode 100644 index 000000000..ab8ea0a2a --- /dev/null +++ b/arclight-forge/patches/README.md @@ -0,0 +1,25 @@ +# Vendored patches — Java 25 runtime compatibility + +`io.izzel.arclight:arclight-api:1.5.4` is consumed as a binary dependency (its source is not +published). To run on Java 25 it needed patching, so the patched class source is kept here for +review/reproducibility and the rebuilt jar is vendored at +`arclight-forge/libs/arclight-api-1.5.4-j25.jar` (referenced by `arclight-forge/build.gradle`). + +## `arclight-api/io/izzel/arclight/api/Unsafe.java` +Patched from the decompiled 1.5.4 class. Changes vs upstream: +- `ensureClassInitialized` / `shouldBeInitialized` are routed to `jdk.internal.misc.Unsafe` + (the `sun.misc.Unsafe` versions were removed in JDK 22+), resolved through the trusted IMPL_LOOKUP. +- The caller-class provider always uses `StackWalker` (the `SecurityManager` constructor throws on + JDK 24+, JEP 486). +- The dead `sun.misc.Unsafe.defineAnonymousClass` fallback branch throws instead (gone since JDK 17). +- Restored the `(Object)` cast on the last `defineAnonymousClass` invokeExact argument (a cast the + decompiler had dropped; without it, plugin event-executor generation threw WrongMethodTypeException). + +### Rebuilding the jar +```bash +# compile only Unsafe.java against asm, then swap its classes into a copy of the original jar +javac -XDignore.symbol.file -cp asm-9.x.jar -d out \ + patches/arclight-api/io/izzel/arclight/api/Unsafe.java +# (extract original arclight-api-1.5.4.jar, replace io/izzel/arclight/api/Unsafe*.class with the +# freshly compiled ones, re-jar as arclight-api-1.5.4-j25.jar) +``` diff --git a/arclight-forge/patches/arclight-api/io/izzel/arclight/api/Unsafe.java b/arclight-forge/patches/arclight-api/io/izzel/arclight/api/Unsafe.java new file mode 100644 index 000000000..c334bc9f8 --- /dev/null +++ b/arclight-forge/patches/arclight-api/io/izzel/arclight/api/Unsafe.java @@ -0,0 +1,522 @@ +package io.izzel.arclight.api; + +import java.lang.StackWalker.Option; +import java.lang.StackWalker.StackFrame; +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; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Objects; +import org.objectweb.asm.ClassReader; + +public class Unsafe { + + private static final sun.misc.Unsafe unsafe; + private static final Lookup lookup; + private static final MethodHandle defineClass; + private static final MethodHandle H_DEF_CLASS; + private static final Unsafe.CallerClass INSTANCE; + // Java 25: ensureClassInitialized/shouldBeInitialized were removed from sun.misc.Unsafe; + // they are sourced from jdk.internal.misc.Unsafe via the trusted lookup instead. + private static final MethodHandle ensureInitMH; + private static final MethodHandle shouldInitMH; + + public static T getStatic(Class cl, String name) { + try { + ensureClassInitialized(cl); + Field field = cl.getDeclaredField(name); + Object materialByNameBase = staticFieldBase(field); + long materialByNameOffset = staticFieldOffset(field); + + return (T) getObject(materialByNameBase, materialByNameOffset); + } catch (Exception exception) { + return null; + } + } + + public static Lookup lookup() { + return Unsafe.lookup; + } + + public static sun.misc.Unsafe getUnsafe() { + return Unsafe.unsafe; + } + + public static int getInt(Object o, long l) { + return Unsafe.unsafe.getInt(o, l); + } + + public static void putInt(Object o, long l, int i) { + Unsafe.unsafe.putInt(o, l, i); + } + + public static Object getObject(Object o, long l) { + return Unsafe.unsafe.getObject(o, l); + } + + public static void putObject(Object o, long l, Object o1) { + Unsafe.unsafe.putObject(o, l, o1); + } + + public static boolean getBoolean(Object o, long l) { + return Unsafe.unsafe.getBoolean(o, l); + } + + public static void putBoolean(Object o, long l, boolean b) { + Unsafe.unsafe.putBoolean(o, l, b); + } + + public static byte getByte(Object o, long l) { + return Unsafe.unsafe.getByte(o, l); + } + + public static void putByte(Object o, long l, byte b) { + Unsafe.unsafe.putByte(o, l, b); + } + + public static short getShort(Object o, long l) { + return Unsafe.unsafe.getShort(o, l); + } + + public static void putShort(Object o, long l, short i) { + Unsafe.unsafe.putShort(o, l, i); + } + + public static char getChar(Object o, long l) { + return Unsafe.unsafe.getChar(o, l); + } + + public static void putChar(Object o, long l, char c) { + Unsafe.unsafe.putChar(o, l, c); + } + + public static long getLong(Object o, long l) { + return Unsafe.unsafe.getLong(o, l); + } + + public static void putLong(Object o, long l, long l1) { + Unsafe.unsafe.putLong(o, l, l1); + } + + public static float getFloat(Object o, long l) { + return Unsafe.unsafe.getFloat(o, l); + } + + public static void putFloat(Object o, long l, float v) { + Unsafe.unsafe.putFloat(o, l, v); + } + + public static double getDouble(Object o, long l) { + return Unsafe.unsafe.getDouble(o, l); + } + + public static void putDouble(Object o, long l, double v) { + Unsafe.unsafe.putDouble(o, l, v); + } + + public static byte getByte(long l) { + return Unsafe.unsafe.getByte(l); + } + + public static void putByte(long l, byte b) { + Unsafe.unsafe.putByte(l, b); + } + + public static short getShort(long l) { + return Unsafe.unsafe.getShort(l); + } + + public static void putShort(long l, short i) { + Unsafe.unsafe.putShort(l, i); + } + + public static char getChar(long l) { + return Unsafe.unsafe.getChar(l); + } + + public static void putChar(long l, char c) { + Unsafe.unsafe.putChar(l, c); + } + + public static int getInt(long l) { + return Unsafe.unsafe.getInt(l); + } + + public static void putInt(long l, int i) { + Unsafe.unsafe.putInt(l, i); + } + + public static long getLong(long l) { + return Unsafe.unsafe.getLong(l); + } + + public static void putLong(long l, long l1) { + Unsafe.unsafe.putLong(l, l1); + } + + public static float getFloat(long l) { + return Unsafe.unsafe.getFloat(l); + } + + public static void putFloat(long l, float v) { + Unsafe.unsafe.putFloat(l, v); + } + + public static double getDouble(long l) { + return Unsafe.unsafe.getDouble(l); + } + + public static void putDouble(long l, double v) { + Unsafe.unsafe.putDouble(l, v); + } + + public static long getAddress(long l) { + return Unsafe.unsafe.getAddress(l); + } + + public static void putAddress(long l, long l1) { + Unsafe.unsafe.putAddress(l, l1); + } + + public static long allocateMemory(long l) { + return Unsafe.unsafe.allocateMemory(l); + } + + public static long reallocateMemory(long l, long l1) { + return Unsafe.unsafe.reallocateMemory(l, l1); + } + + public static void setMemory(Object o, long l, long l1, byte b) { + Unsafe.unsafe.setMemory(o, l, l1, b); + } + + public static void setMemory(long l, long l1, byte b) { + Unsafe.unsafe.setMemory(l, l1, b); + } + + public static void copyMemory(Object o, long l, Object o1, long l1, long l2) { + Unsafe.unsafe.copyMemory(o, l, o1, l1, l2); + } + + public static void copyMemory(long l, long l1, long l2) { + Unsafe.unsafe.copyMemory(l, l1, l2); + } + + public static void freeMemory(long l) { + Unsafe.unsafe.freeMemory(l); + } + + public static long staticFieldOffset(Field field) { + return Unsafe.unsafe.staticFieldOffset(field); + } + + public static long objectFieldOffset(Field field) { + return Unsafe.unsafe.objectFieldOffset(field); + } + + public static Object staticFieldBase(Field field) { + return Unsafe.unsafe.staticFieldBase(field); + } + + public static boolean shouldBeInitialized(Class aClass) { + try { + return (boolean) Unsafe.shouldInitMH.invoke(aClass); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + + public static void ensureClassInitialized(Class aClass) { + try { + Unsafe.ensureInitMH.invoke(aClass); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + + public static int arrayBaseOffset(Class aClass) { + return Unsafe.unsafe.arrayBaseOffset(aClass); + } + + public static int arrayIndexScale(Class aClass) { + return Unsafe.unsafe.arrayIndexScale(aClass); + } + + public static int addressSize() { + return Unsafe.unsafe.addressSize(); + } + + public static int pageSize() { + return Unsafe.unsafe.pageSize(); + } + + public static Class defineClass(String s, byte[] bytes, int i, int i1, ClassLoader classLoader, ProtectionDomain protectionDomain) { + try { + return (Class) Unsafe.defineClass.invokeExact(s, bytes, i, i1, classLoader, protectionDomain); + } catch (Throwable throwable) { + throwException(throwable); + return null; + } + } + + public static Class defineAnonymousClass(Class aClass, byte[] bytes) { + return defineAnonymousClass(aClass, bytes, (Object[]) null); + } + + public static Class defineAnonymousClass(Class aClass, byte[] bytes, Object[] objects) { + if (Unsafe.H_DEF_CLASS != null) { + try { + return (Class) Unsafe.H_DEF_CLASS.invokeExact(aClass.getClassLoader(), aClass, (new ClassReader(bytes)).getClassName(), bytes, 0, bytes.length, aClass.getProtectionDomain(), false, 11, (Object) objects); + } catch (Throwable throwable) { + throwException(throwable); + return null; + } + } else { + // sun.misc.Unsafe.defineAnonymousClass was removed in JDK 17; on modern JDKs the + // H_DEF_CLASS path above is always used, so this branch is unreachable. + throw new UnsupportedOperationException("defineAnonymousClass is unavailable on this JDK"); + } + } + + public static Object allocateInstance(Class aClass) throws InstantiationException { + return Unsafe.unsafe.allocateInstance(aClass); + } + + public static void throwException(Throwable throwable) { + Unsafe.unsafe.throwException(throwable); + } + + public static boolean compareAndSwapObject(Object o, long l, Object o1, Object o2) { + return Unsafe.unsafe.compareAndSwapObject(o, l, o1, o2); + } + + public static boolean compareAndSwapInt(Object o, long l, int i, int i1) { + return Unsafe.unsafe.compareAndSwapInt(o, l, i, i1); + } + + public static boolean compareAndSwapLong(Object o, long l, long l1, long l2) { + return Unsafe.unsafe.compareAndSwapLong(o, l, l1, l2); + } + + public static Object getObjectVolatile(Object o, long l) { + return Unsafe.unsafe.getObjectVolatile(o, l); + } + + public static void putObjectVolatile(Object o, long l, Object o1) { + Unsafe.unsafe.putObjectVolatile(o, l, o1); + } + + public static int getIntVolatile(Object o, long l) { + return Unsafe.unsafe.getIntVolatile(o, l); + } + + public static void putIntVolatile(Object o, long l, int i) { + Unsafe.unsafe.putIntVolatile(o, l, i); + } + + public static boolean getBooleanVolatile(Object o, long l) { + return Unsafe.unsafe.getBooleanVolatile(o, l); + } + + public static void putBooleanVolatile(Object o, long l, boolean b) { + Unsafe.unsafe.putBooleanVolatile(o, l, b); + } + + public static byte getByteVolatile(Object o, long l) { + return Unsafe.unsafe.getByteVolatile(o, l); + } + + public static void putByteVolatile(Object o, long l, byte b) { + Unsafe.unsafe.putByteVolatile(o, l, b); + } + + public static short getShortVolatile(Object o, long l) { + return Unsafe.unsafe.getShortVolatile(o, l); + } + + public static void putShortVolatile(Object o, long l, short i) { + Unsafe.unsafe.putShortVolatile(o, l, i); + } + + public static char getCharVolatile(Object o, long l) { + return Unsafe.unsafe.getCharVolatile(o, l); + } + + public static void putCharVolatile(Object o, long l, char c) { + Unsafe.unsafe.putCharVolatile(o, l, c); + } + + public static long getLongVolatile(Object o, long l) { + return Unsafe.unsafe.getLongVolatile(o, l); + } + + public static void putLongVolatile(Object o, long l, long l1) { + Unsafe.unsafe.putLongVolatile(o, l, l1); + } + + public static float getFloatVolatile(Object o, long l) { + return Unsafe.unsafe.getFloatVolatile(o, l); + } + + public static void putFloatVolatile(Object o, long l, float v) { + Unsafe.unsafe.putFloatVolatile(o, l, v); + } + + public static double getDoubleVolatile(Object o, long l) { + return Unsafe.unsafe.getDoubleVolatile(o, l); + } + + public static void putDoubleVolatile(Object o, long l, double v) { + Unsafe.unsafe.putDoubleVolatile(o, l, v); + } + + public static void putOrderedObject(Object o, long l, Object o1) { + Unsafe.unsafe.putOrderedObject(o, l, o1); + } + + public static void putOrderedInt(Object o, long l, int i) { + Unsafe.unsafe.putOrderedInt(o, l, i); + } + + public static void putOrderedLong(Object o, long l, long l1) { + Unsafe.unsafe.putOrderedLong(o, l, l1); + } + + public static void unpark(Object o) { + Unsafe.unsafe.unpark(o); + } + + public static void park(boolean b, long l) { + Unsafe.unsafe.park(b, l); + } + + public static int getLoadAverage(double[] doubles, int i) { + return Unsafe.unsafe.getLoadAverage(doubles, i); + } + + public static int getAndAddInt(Object o, long l, int i) { + return Unsafe.unsafe.getAndAddInt(o, l, i); + } + + public static long getAndAddLong(Object o, long l, long l1) { + return Unsafe.unsafe.getAndAddLong(o, l, l1); + } + + public static int getAndSetInt(Object o, long l, int i) { + return Unsafe.unsafe.getAndSetInt(o, l, i); + } + + public static long getAndSetLong(Object o, long l, long l1) { + return Unsafe.unsafe.getAndSetLong(o, l, l1); + } + + public static Object getAndSetObject(Object o, long l, Object o1) { + return Unsafe.unsafe.getAndSetObject(o, l, o1); + } + + public static void loadFence() { + Unsafe.unsafe.loadFence(); + } + + public static void storeFence() { + Unsafe.unsafe.storeFence(); + } + + public static void fullFence() { + Unsafe.unsafe.fullFence(); + } + + public static Class getCallerClass() { + return Unsafe.INSTANCE.getCallerClass(); + } + + static { + try { + Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + + theUnsafe.setAccessible(true); + unsafe = (sun.misc.Unsafe) theUnsafe.get((Object) null); + + // Lookup is already initialized by the JVM at this point; read the trusted IMPL_LOOKUP + // directly (the old ensureClassInitialized(Lookup.class) used a now-removed method). + Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP"); + Object base = Unsafe.unsafe.staticFieldBase(field); + long offset = Unsafe.unsafe.staticFieldOffset(field); + + lookup = (Lookup) Unsafe.unsafe.getObject(base, offset); + + // jdk.internal.misc.Unsafe instance, read via sun.misc.Unsafe to bypass module checks. + Class jdkInternalUnsafe = Class.forName("jdk.internal.misc.Unsafe"); + Field internalUnsafeField = jdkInternalUnsafe.getDeclaredField("theUnsafe"); + Object internalUnsafe = Unsafe.unsafe.getObject(Unsafe.unsafe.staticFieldBase(internalUnsafeField), Unsafe.unsafe.staticFieldOffset(internalUnsafeField)); + + // ensureClassInitialized / shouldBeInitialized were removed from sun.misc.Unsafe (JDK 22+); + // route them to jdk.internal.misc.Unsafe through the trusted lookup. + ensureInitMH = Unsafe.lookup.unreflect(jdkInternalUnsafe.getMethod("ensureClassInitialized", Class.class)).bindTo(internalUnsafe); + shouldInitMH = Unsafe.lookup.unreflect(jdkInternalUnsafe.getMethod("shouldBeInitialized", Class.class)).bindTo(internalUnsafe); + + MethodHandle mh; + + try { + Method sunMisc = Unsafe.unsafe.getClass().getMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE, ClassLoader.class, ProtectionDomain.class); + + mh = Unsafe.lookup.unreflect(sunMisc).bindTo(Unsafe.unsafe); + } catch (Exception exception) { + Method internalDefineClass = jdkInternalUnsafe.getMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE, ClassLoader.class, ProtectionDomain.class); + + mh = Unsafe.lookup.unreflect(internalDefineClass).bindTo(internalUnsafe); + } + + defineClass = (MethodHandle) Objects.requireNonNull(mh); + } catch (Exception exception1) { + throw new RuntimeException(exception1); + } + + MethodHandle handle; + + try { + handle = lookup().findStatic(ClassLoader.class, "defineClass0", MethodType.methodType(Class.class, ClassLoader.class, Class.class, String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class, Boolean.TYPE, Integer.TYPE, Object.class)); + } catch (Throwable throwable) { + try { + handle = MethodHandles.dropArguments(lookup().findVirtual(ClassLoader.class, "defineClassInternal", MethodType.methodType(Class.class, Class.class, String.class, byte[].class, ProtectionDomain.class, Boolean.TYPE, Integer.TYPE, Object.class)), 4, Arrays.asList(Integer.TYPE, Integer.TYPE)); + } catch (Throwable throwable1) { + handle = null; + } + } + + H_DEF_CLASS = handle; + + // StackWalker works on Java 9+ and avoids SecurityManager, whose constructor throws + // on JDK 24+ (the Security Manager was permanently disabled by JEP 486). + INSTANCE = new Unsafe.StackWalkerCallerClassimplements(); + } + + private interface CallerClass { + + Class getCallerClass(); + } + + private static class StackWalkerCallerClassimplements implements Unsafe.CallerClass { + + private final StackWalker walker; + + private StackWalkerCallerClassimplements() { + this.walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); + } + + public Class getCallerClass() { + return (Class) this.walker.walk((sx) -> { + return ((StackFrame) sx.skip(3L).findFirst().orElseThrow()).getDeclaringClass(); + }); + } + + StackWalkerCallerClassimplements(Object x0) { + this(); + } + } + +}