From 5af50681ced3577bc0a691722a088af1698de7c3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 15 Jan 2026 14:09:11 +0800 Subject: [PATCH 1/5] First around --- build.gradle.kts | 3 +- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 5 +- .../gradle/plugins/shadow/BasePluginTest.kt | 5 +- .../gradle/plugins/shadow/RelocationTest.kt | 8 +- .../shadow/internal/ClassFileHelper.java | 266 ++++++++++++++++++ .../shadow/internal/RelocatorRemapper.kt | 25 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 25 +- 8 files changed, 309 insertions(+), 30 deletions(-) create mode 100644 src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java diff --git a/build.gradle.kts b/build.gradle.kts index 256d1186b..7526ef8da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,6 +27,7 @@ description = providers.gradleProperty("POM_DESCRIPTION").get() dokka { dokkaPublications.html { outputDirectory = rootDir.resolve("docs/api") } } kotlin { + jvmToolchain(libs.versions.jdkRelease.get().toInt()) explicitApi() @OptIn(ExperimentalAbiValidation::class) abiValidation { enabled = true } compilerOptions { @@ -36,7 +37,6 @@ kotlin { languageVersion = apiVersion jvmTarget = JvmTarget.fromTarget(libs.versions.jdkRelease.get()) jvmDefault = JvmDefaultMode.NO_COMPATIBILITY - freeCompilerArgs.add("-Xjdk-release=${libs.versions.jdkRelease.get()}") } } @@ -219,6 +219,7 @@ kotlin.target.compilations { tasks.withType().configureEach { options.release = libs.versions.jdkRelease.get().toInt() + options.compilerArgs.add("--enable-preview") } tasks.pluginUnderTestMetadata { pluginClasspath.from(testPluginClasspath) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 58d9b4779..e28e0c4b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -jdkRelease = "17" +jdkRelease = "24" minGradle = "9.0.0" kotlin = "2.3.20-Beta1" moshi = "1.15.2" diff --git a/settings.gradle.kts b/settings.gradle.kts index 93027b77d..34dd46553 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,7 +12,10 @@ pluginManagement { } } -plugins { id("com.gradle.develocity") version "4.3" } +plugins { + id("com.gradle.develocity") version "4.3" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" +} develocity { buildScan { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index 01b1a3d42..b29d70d4c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -380,7 +380,10 @@ abstract class BasePluginTest { return gradleRunner( projectDir = projectRoot, arguments = commonGradleArgs + arguments, - block = block, + block = { + forwardOutput() + block() + }, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 852ebcc79..25e4a991a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -32,6 +32,8 @@ import org.junit.jupiter.params.provider.ValueSource import org.opentest4j.AssertionFailedError class RelocationTest : BasePluginTest() { + private val javaExec = System.getProperty("java.home") + "/bin/java" + @ParameterizedTest @ValueSource(strings = ["foo", "new.pkg", "new/path"]) fun autoRelocation(relocationPrefix: String) { @@ -496,7 +498,7 @@ class RelocationTest : BasePluginTest() { ) runWithSuccess(shadowJarPath) - val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) + val result = runProcess(javaExec, "-jar", outputShadowedJar.use { it.toString() }) assertThat(result).contains("shadow.foo.Foo", "shadow.foo.Bar") } @@ -524,7 +526,7 @@ class RelocationTest : BasePluginTest() { ) runWithSuccess(shadowJarPath) - val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) + val result = runProcess(javaExec, "-jar", outputShadowedJar.use { it.toString() }) if (skipStringConstants) { assertThat(result).contains("foo.Foo", "foo.Bar") @@ -562,7 +564,7 @@ class RelocationTest : BasePluginTest() { ) runWithSuccess(shadowJarPath) - val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) + val result = runProcess(javaExec, "-jar", outputShadowedJar.use { it.toString() }) // Just check that the jar can be executed without NoClassDefFoundError. assertThat(result) diff --git a/src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java b/src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java new file mode 100644 index 000000000..225e7cc1a --- /dev/null +++ b/src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java @@ -0,0 +1,266 @@ +package com.github.jengelman.gradle.plugins.shadow.internal; + +import java.lang.classfile.ClassBuilder; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.ClassSignature; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeTransform; +import java.lang.classfile.FieldModel; +import java.lang.classfile.Interfaces; +import java.lang.classfile.MethodModel; +import java.lang.classfile.MethodSignature; +import java.lang.classfile.Opcode; +import java.lang.classfile.Signature; +import java.lang.classfile.Superclass; +import java.lang.classfile.TypeAnnotation; +import java.lang.classfile.attribute.CodeAttribute; +import java.lang.classfile.attribute.SignatureAttribute; +import java.lang.classfile.constantpool.ClassEntry; +import java.lang.classfile.constantpool.StringEntry; +import java.lang.classfile.instruction.ConstantInstruction; +import java.lang.classfile.instruction.FieldInstruction; +import java.lang.classfile.instruction.InvokeInstruction; +import java.lang.classfile.instruction.NewObjectInstruction; +import java.lang.classfile.instruction.NewReferenceArrayInstruction; +import java.lang.classfile.instruction.TypeCheckInstruction; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ClassFileHelper { + + public static byte[] remapClass(byte[] classBytes, Function mapFunction, + Function mapValueFunction) { + ClassFile cc = ClassFile.of(); + ClassModel classModel = cc.parse(classBytes); + return cc.transformClass(classModel, (ClassTransform) new SimpleClassRemapper(mapFunction, mapValueFunction)); + } + + private static class SimpleClassRemapper implements ClassTransform { + private final Function mapFunction; + private final Function mapValueFunction; + + public SimpleClassRemapper(Function mapFunction, Function mapValueFunction) { + this.mapFunction = mapFunction; + this.mapValueFunction = mapValueFunction; + } + + @Override + public void accept(ClassBuilder builder, ClassElement element) { + if (element instanceof FieldModel fm) { + // Remap field descriptor + ClassDesc oldDesc = fm.fieldTypeSymbol(); + ClassDesc newDesc = mapFunction.apply(oldDesc); + builder.withField(fm.fieldName().stringValue(), newDesc, fb -> { + fb.withFlags(fm.flags().flagsMask()); + for (var elem : fm) { + if (elem instanceof SignatureAttribute sa) { + // Field Signature -> TypeSignature + fb.with(SignatureAttribute + .of(remapSignature(Signature.parseFrom(sa.signature().stringValue()), mapFunction))); + } else { + fb.with(elem); + } + } + }); + } else if (element instanceof MethodModel mm) { + // Remap method descriptor + MethodTypeDesc oldDesc = mm.methodTypeSymbol(); + MethodTypeDesc newDesc = mapMethodDesc(oldDesc, mapFunction); + builder.withMethod(mm.methodName().stringValue(), newDesc, mm.flags().flagsMask(), mb -> { + for (var elem : mm) { + if (elem instanceof CodeAttribute code) { + mb.transformCode(code, new SimpleCodeRemapper(mapFunction, mapValueFunction)); + } else if (elem instanceof SignatureAttribute sa) { + // Method Signature -> MethodSignature + try { + MethodSignature ms = MethodSignature.parseFrom(sa.signature().stringValue()); + mb.with(SignatureAttribute.of(remapMethodSignature(ms, mapFunction))); + } catch (Exception e) { + e.printStackTrace(); + // Fallback or rethrow? Usually parseFrom shouldn't fail on valid class. + // If it's not a MethodSignature (e.g. malformed), copy original + mb.with(elem); + } + } else { + mb.with(elem); + } + } + }); + } else if (element instanceof Superclass sc) { + // Remap superclass + ClassDesc newSc = mapFunction.apply(sc.superclassEntry().asSymbol()); + builder.withSuperclass(builder.constantPool().classEntry(newSc)); + } else if (element instanceof Interfaces ifaces) { + List newInterfaces = new ArrayList<>(); + for (ClassEntry iface : ifaces.interfaces()) { + newInterfaces.add(builder.constantPool().classEntry(mapFunction.apply(iface.asSymbol()))); + } + builder.withInterfaces(newInterfaces); + } else if (element instanceof SignatureAttribute sa) { + // Class Signature + try { + ClassSignature cs = ClassSignature.parseFrom(sa.signature().stringValue()); + builder.with(SignatureAttribute.of(remapClassSignature(cs, mapFunction))); + } catch (Exception e) { + e.printStackTrace(); + builder.with(element); + } + } else { + builder.with(element); + } + } + } + + private static class SimpleCodeRemapper implements CodeTransform { + private final Function mapFunction; + private final Function mapValueFunction; + + public SimpleCodeRemapper(Function mapFunction, Function mapValueFunction) { + this.mapFunction = mapFunction; + this.mapValueFunction = mapValueFunction; + } + + @Override + public void accept(CodeBuilder builder, CodeElement element) { + if (element instanceof TypeCheckInstruction tci) { // CHECKCAST, INSTANCEOF + ClassDesc newType = mapFunction.apply(tci.type().asSymbol()); + if (tci.opcode() == Opcode.CHECKCAST) { + builder.checkcast(newType); + } else if (tci.opcode() == Opcode.INSTANCEOF) { + builder.instanceOf(newType); + } else { + builder.with(element); // fallback + } + } else if (element instanceof NewObjectInstruction noi) { + builder.new_(mapFunction.apply(noi.className().asSymbol())); + } else if (element instanceof NewReferenceArrayInstruction nrai) { + builder.anewarray(mapFunction.apply(nrai.componentType().asSymbol())); + } else if (element instanceof FieldInstruction fi) { + ClassDesc newOwner = mapFunction.apply(fi.owner().asSymbol()); + ClassDesc newType = mapFunction.apply(fi.typeSymbol()); + String name = fi.name().stringValue(); + Opcode op = fi.opcode(); + if (op == Opcode.GETFIELD) + builder.getfield(newOwner, name, newType); + else if (op == Opcode.PUTFIELD) + builder.putfield(newOwner, name, newType); + else if (op == Opcode.GETSTATIC) + builder.getstatic(newOwner, name, newType); + else if (op == Opcode.PUTSTATIC) + builder.putstatic(newOwner, name, newType); + else + builder.with(element); + } else if (element instanceof InvokeInstruction ii) { + ClassDesc newOwner = mapFunction.apply(ii.owner().asSymbol()); + MethodTypeDesc newDesc = mapMethodDesc(ii.typeSymbol(), mapFunction); + String name = ii.name().stringValue(); + boolean isInterface = ii.isInterface(); + Opcode op = ii.opcode(); + if (op == Opcode.INVOKEVIRTUAL) + builder.invokevirtual(newOwner, name, newDesc); + else if (op == Opcode.INVOKESPECIAL) + builder.invokespecial(newOwner, name, newDesc, isInterface); + else if (op == Opcode.INVOKESTATIC) + builder.invokestatic(newOwner, name, newDesc, isInterface); + else if (op == Opcode.INVOKEINTERFACE) + builder.invokeinterface(newOwner, name, newDesc); + else + builder.with(element); + } else if (element instanceof ConstantInstruction.LoadConstantInstruction lci) { + if (lci.constantEntry() instanceof ClassEntry ce) { + builder.ldc(mapFunction.apply(ce.asSymbol())); + } else if (lci.constantEntry() instanceof StringEntry se) { + builder.ldc(mapValueFunction.apply(se.stringValue())); + } else { + builder.with(element); + } + } else { + builder.with(element); + } + } + } + + private static MethodTypeDesc mapMethodDesc(MethodTypeDesc desc, Function mapFunction) { + ClassDesc newReturnType = mapFunction.apply(desc.returnType()); + ClassDesc[] newParamTypes = desc.parameterList().stream().map(mapFunction).toArray(ClassDesc[]::new); + return MethodTypeDesc.of(newReturnType, newParamTypes); + } + + private static ClassSignature remapClassSignature(ClassSignature cs, Function mapFunction) { + return ClassSignature.of( + remapTypeParams(cs.typeParameters(), mapFunction), // Expecting List + (Signature.ClassTypeSig) remapSignature(cs.superclassSignature(), mapFunction), + cs.superinterfaceSignatures().stream().map(s -> (Signature.ClassTypeSig) remapSignature(s, mapFunction)) + .toArray(Signature.ClassTypeSig[]::new)); + } + + private static MethodSignature remapMethodSignature(MethodSignature ms, Function mapFunction) { + return MethodSignature.of( + remapTypeParams(ms.typeParameters(), mapFunction), // Expecting List + ms.throwableSignatures().stream().map(s -> (Signature.ThrowableSig) remapSignature(s, mapFunction)) + .collect(Collectors.toList()), + remapSignature(ms.result(), mapFunction), + ms.arguments().stream().map(s -> remapSignature(s, mapFunction)).toArray(Signature[]::new)); + } + + private static Signature remapSignature(Signature sig, Function mapFunction) { + System.err.println("DEBUG: remapSignature " + sig.getClass().getSimpleName()); + if (sig instanceof Signature.ClassTypeSig cts) { + ClassDesc newDesc = mapFunction.apply(cts.classDesc()); + List newArgs = new ArrayList<>(); + for (Signature.TypeArg arg : cts.typeArgs()) { + if (arg instanceof Signature.TypeArg.Bounded boundedArg) { + Signature.RefTypeSig newBound = (Signature.RefTypeSig) remapSignature(boundedArg.boundType(), mapFunction); + Signature.TypeArg.Bounded.WildcardIndicator indicator = boundedArg.wildcardIndicator(); + if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.NONE) { + newArgs.add(Signature.TypeArg.of(newBound)); + } else if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.EXTENDS) { + newArgs.add(Signature.TypeArg.extendsOf(newBound)); + } else if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.SUPER) { + newArgs.add(Signature.TypeArg.superOf(newBound)); + } + } else if (arg instanceof Signature.TypeArg.Unbounded) { + newArgs.add(arg); + } + } + Signature.ClassTypeSig outer = cts.outerType().orElse(null); + Signature.ClassTypeSig newOuter = outer != null ? (Signature.ClassTypeSig) remapSignature(outer, mapFunction) + : null; + if (newOuter != null) { + return Signature.ClassTypeSig.of(newOuter, newDesc, newArgs.toArray(Signature.TypeArg[]::new)); + } else { + return Signature.ClassTypeSig.of(newDesc, newArgs.toArray(Signature.TypeArg[]::new)); + } + } else if (sig instanceof Signature.ArrayTypeSig ats) { + return Signature.ArrayTypeSig.of(remapSignature(ats.componentSignature(), mapFunction)); + } else if (sig instanceof Signature.TypeVarSig) { + return sig; // Keep type var name + } else if (sig instanceof Signature.BaseTypeSig) { + return sig; + } + return sig; + } + + private static List remapTypeParams(List params, + Function mapFunction) { + List newParams = new ArrayList<>(); + for (Signature.TypeParam p : params) { + Signature.RefTypeSig newClassBound = p.classBound().isPresent() + ? (Signature.RefTypeSig) remapSignature(p.classBound().get(), mapFunction) + : null; + List newInterfaceBounds = p.interfaceBounds().stream() + .map(s -> (Signature.RefTypeSig) remapSignature(s, mapFunction)).collect(Collectors.toList()); + newParams.add(Signature.TypeParam.of(p.identifier(), newClassBound, + newInterfaceBounds.toArray(Signature.RefTypeSig[]::new))); + } + return newParams; + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index c10dddab5..f652e22f2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -3,9 +3,8 @@ package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import com.github.jengelman.gradle.plugins.shadow.relocation.relocatePath +import java.lang.constant.ClassDesc import java.util.regex.Pattern -import org.vafer.jdeb.shaded.objectweb.asm.Opcodes -import org.vafer.jdeb.shaded.objectweb.asm.commons.Remapper /** * Modified from @@ -18,17 +17,27 @@ import org.vafer.jdeb.shaded.objectweb.asm.commons.Remapper internal class RelocatorRemapper( private val relocators: Set, private val onModified: () -> Unit = {}, -) : Remapper(Opcodes.ASM9) { +) { - override fun mapValue(value: Any): Any { - return if (value is String) { - mapName(value, mapLiterals = true) + fun map(desc: ClassDesc): ClassDesc { + val descriptor = desc.descriptorString() + // We only map class types (L...;), not primitives. + if (descriptor.length < 3 || descriptor[0] != 'L') return desc + + // Extract internal name: Lcom/example/Foo; -> com/example/Foo + val internalName = descriptor.substring(1, descriptor.length - 1) + val newInternalName = mapName(internalName, mapLiterals = false) + + return if (newInternalName != internalName) { + ClassDesc.ofDescriptor("L$newInternalName;") } else { - super.mapValue(value) + desc } } - override fun map(internalName: String): String = mapName(internalName) + fun mapValue(value: String): String { + return mapName(value, true) + } private fun mapName(name: String, mapLiterals: Boolean = false): String { // Maybe a list of types. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 9b8c0c933..ca9f41c9a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -4,6 +4,7 @@ package com.github.jengelman.gradle.plugins.shadow.tasks +import com.github.jengelman.gradle.plugins.shadow.internal.ClassFileHelper import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.cast import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry @@ -32,9 +33,6 @@ import org.gradle.api.internal.file.copy.FileCopyDetailsInternal import org.gradle.api.logging.Logging import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults -import org.vafer.jdeb.shaded.objectweb.asm.ClassReader -import org.vafer.jdeb.shaded.objectweb.asm.ClassWriter -import org.vafer.jdeb.shaded.objectweb.asm.commons.ClassRemapper /** * Modified from @@ -222,19 +220,16 @@ constructor( // constant pool are never used), but confuses some tools such as Felix's // maven-bundle-plugin // that use the constant pool to determine the dependencies of a class. - val cw = ClassWriter(0) - val cr = ClassReader(bytes) - val cv = ClassRemapper(cw, remapper) - - try { - cr.accept(cv, ClassReader.EXPAND_FRAMES) - } catch (t: Throwable) { - throw GradleException("Error in ASM processing class $path", t) - } - val newBytes = + try { + ClassFileHelper.remapClass(bytes, remapper::map, remapper::mapValue) + } catch (t: Throwable) { + throw GradleException("Error in Class-File API processing class $path", t) + } + + val resultBytes = if (modified) { - cw.toByteArray() + newBytes } else { // If we didn't need to change anything, keep the original bytes as-is bytes @@ -251,7 +246,7 @@ constructor( } // Now we put it back on so the class file is written out with the right extension. zipOutStr.putNextEntry(entry) - zipOutStr.write(newBytes) + zipOutStr.write(resultBytes) zipOutStr.closeEntry() } catch (_: ZipException) { logger.warn("We have a duplicate $relocatedPath in source project") From 51e6eb1ed07fffb63d88013d415ee631f1cc5cde Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 15 Jan 2026 14:18:26 +0800 Subject: [PATCH 2/5] Convert ClassFileHelper to Kotlin --- .../shadow/internal/ClassFileHelper.java | 266 --------------- .../shadow/internal/ClassFileHelper.kt | 303 ++++++++++++++++++ 2 files changed, 303 insertions(+), 266 deletions(-) delete mode 100644 src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt diff --git a/src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java b/src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java deleted file mode 100644 index 225e7cc1a..000000000 --- a/src/main/java/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.java +++ /dev/null @@ -1,266 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal; - -import java.lang.classfile.ClassBuilder; -import java.lang.classfile.ClassElement; -import java.lang.classfile.ClassFile; -import java.lang.classfile.ClassModel; -import java.lang.classfile.ClassSignature; -import java.lang.classfile.ClassTransform; -import java.lang.classfile.CodeBuilder; -import java.lang.classfile.CodeElement; -import java.lang.classfile.CodeTransform; -import java.lang.classfile.FieldModel; -import java.lang.classfile.Interfaces; -import java.lang.classfile.MethodModel; -import java.lang.classfile.MethodSignature; -import java.lang.classfile.Opcode; -import java.lang.classfile.Signature; -import java.lang.classfile.Superclass; -import java.lang.classfile.TypeAnnotation; -import java.lang.classfile.attribute.CodeAttribute; -import java.lang.classfile.attribute.SignatureAttribute; -import java.lang.classfile.constantpool.ClassEntry; -import java.lang.classfile.constantpool.StringEntry; -import java.lang.classfile.instruction.ConstantInstruction; -import java.lang.classfile.instruction.FieldInstruction; -import java.lang.classfile.instruction.InvokeInstruction; -import java.lang.classfile.instruction.NewObjectInstruction; -import java.lang.classfile.instruction.NewReferenceArrayInstruction; -import java.lang.classfile.instruction.TypeCheckInstruction; -import java.lang.constant.ClassDesc; -import java.lang.constant.MethodTypeDesc; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class ClassFileHelper { - - public static byte[] remapClass(byte[] classBytes, Function mapFunction, - Function mapValueFunction) { - ClassFile cc = ClassFile.of(); - ClassModel classModel = cc.parse(classBytes); - return cc.transformClass(classModel, (ClassTransform) new SimpleClassRemapper(mapFunction, mapValueFunction)); - } - - private static class SimpleClassRemapper implements ClassTransform { - private final Function mapFunction; - private final Function mapValueFunction; - - public SimpleClassRemapper(Function mapFunction, Function mapValueFunction) { - this.mapFunction = mapFunction; - this.mapValueFunction = mapValueFunction; - } - - @Override - public void accept(ClassBuilder builder, ClassElement element) { - if (element instanceof FieldModel fm) { - // Remap field descriptor - ClassDesc oldDesc = fm.fieldTypeSymbol(); - ClassDesc newDesc = mapFunction.apply(oldDesc); - builder.withField(fm.fieldName().stringValue(), newDesc, fb -> { - fb.withFlags(fm.flags().flagsMask()); - for (var elem : fm) { - if (elem instanceof SignatureAttribute sa) { - // Field Signature -> TypeSignature - fb.with(SignatureAttribute - .of(remapSignature(Signature.parseFrom(sa.signature().stringValue()), mapFunction))); - } else { - fb.with(elem); - } - } - }); - } else if (element instanceof MethodModel mm) { - // Remap method descriptor - MethodTypeDesc oldDesc = mm.methodTypeSymbol(); - MethodTypeDesc newDesc = mapMethodDesc(oldDesc, mapFunction); - builder.withMethod(mm.methodName().stringValue(), newDesc, mm.flags().flagsMask(), mb -> { - for (var elem : mm) { - if (elem instanceof CodeAttribute code) { - mb.transformCode(code, new SimpleCodeRemapper(mapFunction, mapValueFunction)); - } else if (elem instanceof SignatureAttribute sa) { - // Method Signature -> MethodSignature - try { - MethodSignature ms = MethodSignature.parseFrom(sa.signature().stringValue()); - mb.with(SignatureAttribute.of(remapMethodSignature(ms, mapFunction))); - } catch (Exception e) { - e.printStackTrace(); - // Fallback or rethrow? Usually parseFrom shouldn't fail on valid class. - // If it's not a MethodSignature (e.g. malformed), copy original - mb.with(elem); - } - } else { - mb.with(elem); - } - } - }); - } else if (element instanceof Superclass sc) { - // Remap superclass - ClassDesc newSc = mapFunction.apply(sc.superclassEntry().asSymbol()); - builder.withSuperclass(builder.constantPool().classEntry(newSc)); - } else if (element instanceof Interfaces ifaces) { - List newInterfaces = new ArrayList<>(); - for (ClassEntry iface : ifaces.interfaces()) { - newInterfaces.add(builder.constantPool().classEntry(mapFunction.apply(iface.asSymbol()))); - } - builder.withInterfaces(newInterfaces); - } else if (element instanceof SignatureAttribute sa) { - // Class Signature - try { - ClassSignature cs = ClassSignature.parseFrom(sa.signature().stringValue()); - builder.with(SignatureAttribute.of(remapClassSignature(cs, mapFunction))); - } catch (Exception e) { - e.printStackTrace(); - builder.with(element); - } - } else { - builder.with(element); - } - } - } - - private static class SimpleCodeRemapper implements CodeTransform { - private final Function mapFunction; - private final Function mapValueFunction; - - public SimpleCodeRemapper(Function mapFunction, Function mapValueFunction) { - this.mapFunction = mapFunction; - this.mapValueFunction = mapValueFunction; - } - - @Override - public void accept(CodeBuilder builder, CodeElement element) { - if (element instanceof TypeCheckInstruction tci) { // CHECKCAST, INSTANCEOF - ClassDesc newType = mapFunction.apply(tci.type().asSymbol()); - if (tci.opcode() == Opcode.CHECKCAST) { - builder.checkcast(newType); - } else if (tci.opcode() == Opcode.INSTANCEOF) { - builder.instanceOf(newType); - } else { - builder.with(element); // fallback - } - } else if (element instanceof NewObjectInstruction noi) { - builder.new_(mapFunction.apply(noi.className().asSymbol())); - } else if (element instanceof NewReferenceArrayInstruction nrai) { - builder.anewarray(mapFunction.apply(nrai.componentType().asSymbol())); - } else if (element instanceof FieldInstruction fi) { - ClassDesc newOwner = mapFunction.apply(fi.owner().asSymbol()); - ClassDesc newType = mapFunction.apply(fi.typeSymbol()); - String name = fi.name().stringValue(); - Opcode op = fi.opcode(); - if (op == Opcode.GETFIELD) - builder.getfield(newOwner, name, newType); - else if (op == Opcode.PUTFIELD) - builder.putfield(newOwner, name, newType); - else if (op == Opcode.GETSTATIC) - builder.getstatic(newOwner, name, newType); - else if (op == Opcode.PUTSTATIC) - builder.putstatic(newOwner, name, newType); - else - builder.with(element); - } else if (element instanceof InvokeInstruction ii) { - ClassDesc newOwner = mapFunction.apply(ii.owner().asSymbol()); - MethodTypeDesc newDesc = mapMethodDesc(ii.typeSymbol(), mapFunction); - String name = ii.name().stringValue(); - boolean isInterface = ii.isInterface(); - Opcode op = ii.opcode(); - if (op == Opcode.INVOKEVIRTUAL) - builder.invokevirtual(newOwner, name, newDesc); - else if (op == Opcode.INVOKESPECIAL) - builder.invokespecial(newOwner, name, newDesc, isInterface); - else if (op == Opcode.INVOKESTATIC) - builder.invokestatic(newOwner, name, newDesc, isInterface); - else if (op == Opcode.INVOKEINTERFACE) - builder.invokeinterface(newOwner, name, newDesc); - else - builder.with(element); - } else if (element instanceof ConstantInstruction.LoadConstantInstruction lci) { - if (lci.constantEntry() instanceof ClassEntry ce) { - builder.ldc(mapFunction.apply(ce.asSymbol())); - } else if (lci.constantEntry() instanceof StringEntry se) { - builder.ldc(mapValueFunction.apply(se.stringValue())); - } else { - builder.with(element); - } - } else { - builder.with(element); - } - } - } - - private static MethodTypeDesc mapMethodDesc(MethodTypeDesc desc, Function mapFunction) { - ClassDesc newReturnType = mapFunction.apply(desc.returnType()); - ClassDesc[] newParamTypes = desc.parameterList().stream().map(mapFunction).toArray(ClassDesc[]::new); - return MethodTypeDesc.of(newReturnType, newParamTypes); - } - - private static ClassSignature remapClassSignature(ClassSignature cs, Function mapFunction) { - return ClassSignature.of( - remapTypeParams(cs.typeParameters(), mapFunction), // Expecting List - (Signature.ClassTypeSig) remapSignature(cs.superclassSignature(), mapFunction), - cs.superinterfaceSignatures().stream().map(s -> (Signature.ClassTypeSig) remapSignature(s, mapFunction)) - .toArray(Signature.ClassTypeSig[]::new)); - } - - private static MethodSignature remapMethodSignature(MethodSignature ms, Function mapFunction) { - return MethodSignature.of( - remapTypeParams(ms.typeParameters(), mapFunction), // Expecting List - ms.throwableSignatures().stream().map(s -> (Signature.ThrowableSig) remapSignature(s, mapFunction)) - .collect(Collectors.toList()), - remapSignature(ms.result(), mapFunction), - ms.arguments().stream().map(s -> remapSignature(s, mapFunction)).toArray(Signature[]::new)); - } - - private static Signature remapSignature(Signature sig, Function mapFunction) { - System.err.println("DEBUG: remapSignature " + sig.getClass().getSimpleName()); - if (sig instanceof Signature.ClassTypeSig cts) { - ClassDesc newDesc = mapFunction.apply(cts.classDesc()); - List newArgs = new ArrayList<>(); - for (Signature.TypeArg arg : cts.typeArgs()) { - if (arg instanceof Signature.TypeArg.Bounded boundedArg) { - Signature.RefTypeSig newBound = (Signature.RefTypeSig) remapSignature(boundedArg.boundType(), mapFunction); - Signature.TypeArg.Bounded.WildcardIndicator indicator = boundedArg.wildcardIndicator(); - if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.NONE) { - newArgs.add(Signature.TypeArg.of(newBound)); - } else if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.EXTENDS) { - newArgs.add(Signature.TypeArg.extendsOf(newBound)); - } else if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.SUPER) { - newArgs.add(Signature.TypeArg.superOf(newBound)); - } - } else if (arg instanceof Signature.TypeArg.Unbounded) { - newArgs.add(arg); - } - } - Signature.ClassTypeSig outer = cts.outerType().orElse(null); - Signature.ClassTypeSig newOuter = outer != null ? (Signature.ClassTypeSig) remapSignature(outer, mapFunction) - : null; - if (newOuter != null) { - return Signature.ClassTypeSig.of(newOuter, newDesc, newArgs.toArray(Signature.TypeArg[]::new)); - } else { - return Signature.ClassTypeSig.of(newDesc, newArgs.toArray(Signature.TypeArg[]::new)); - } - } else if (sig instanceof Signature.ArrayTypeSig ats) { - return Signature.ArrayTypeSig.of(remapSignature(ats.componentSignature(), mapFunction)); - } else if (sig instanceof Signature.TypeVarSig) { - return sig; // Keep type var name - } else if (sig instanceof Signature.BaseTypeSig) { - return sig; - } - return sig; - } - - private static List remapTypeParams(List params, - Function mapFunction) { - List newParams = new ArrayList<>(); - for (Signature.TypeParam p : params) { - Signature.RefTypeSig newClassBound = p.classBound().isPresent() - ? (Signature.RefTypeSig) remapSignature(p.classBound().get(), mapFunction) - : null; - List newInterfaceBounds = p.interfaceBounds().stream() - .map(s -> (Signature.RefTypeSig) remapSignature(s, mapFunction)).collect(Collectors.toList()); - newParams.add(Signature.TypeParam.of(p.identifier(), newClassBound, - newInterfaceBounds.toArray(Signature.RefTypeSig[]::new))); - } - return newParams; - } -} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt new file mode 100644 index 000000000..df135d36d --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt @@ -0,0 +1,303 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import java.lang.classfile.ClassBuilder +import java.lang.classfile.ClassElement +import java.lang.classfile.ClassFile +import java.lang.classfile.ClassSignature +import java.lang.classfile.ClassTransform +import java.lang.classfile.CodeBuilder +import java.lang.classfile.CodeElement +import java.lang.classfile.CodeTransform +import java.lang.classfile.FieldModel +import java.lang.classfile.Interfaces +import java.lang.classfile.MethodModel +import java.lang.classfile.MethodSignature +import java.lang.classfile.Opcode +import java.lang.classfile.Signature +import java.lang.classfile.Superclass +import java.lang.classfile.attribute.CodeAttribute +import java.lang.classfile.attribute.SignatureAttribute +import java.lang.classfile.constantpool.ClassEntry +import java.lang.classfile.constantpool.StringEntry +import java.lang.classfile.instruction.ConstantInstruction +import java.lang.classfile.instruction.FieldInstruction +import java.lang.classfile.instruction.InvokeInstruction +import java.lang.classfile.instruction.NewObjectInstruction +import java.lang.classfile.instruction.NewReferenceArrayInstruction +import java.lang.classfile.instruction.TypeCheckInstruction +import java.lang.constant.ClassDesc +import java.lang.constant.MethodTypeDesc +import java.util.function.Function + +internal object ClassFileHelper { + + fun remapClass( + classBytes: ByteArray, + mapFunction: Function, + mapValueFunction: Function, + ): ByteArray { + val cc = ClassFile.of() + val classModel = cc.parse(classBytes) + return cc.transformClass(classModel, SimpleClassRemapper(mapFunction, mapValueFunction)) + } + + private class SimpleClassRemapper( + private val mapFunction: Function, + private val mapValueFunction: Function, + ) : ClassTransform { + override fun accept(builder: ClassBuilder, element: ClassElement) { + when (element) { + is FieldModel -> { + // Remap field descriptor + val oldDesc = element.fieldTypeSymbol() + val newDesc = mapFunction.apply(oldDesc) + builder.withField(element.fieldName().stringValue(), newDesc) { fb -> + fb.withFlags(element.flags().flagsMask()) + for (elem in element) { + if (elem is SignatureAttribute) { + // Field Signature -> TypeSignature + fb.with( + SignatureAttribute.of( + remapSignature(Signature.parseFrom(elem.signature().stringValue()), mapFunction) + ) + ) + } else { + fb.with(elem) + } + } + } + } + is MethodModel -> { + // Remap method descriptor + val oldDesc = element.methodTypeSymbol() + val newDesc = mapMethodDesc(oldDesc, mapFunction) + builder.withMethod( + element.methodName().stringValue(), + newDesc, + element.flags().flagsMask(), + ) { mb -> + for (elem in element) { + if (elem is CodeAttribute) { + mb.transformCode(elem, SimpleCodeRemapper(mapFunction, mapValueFunction)) + } else if (elem is SignatureAttribute) { + // Method Signature -> MethodSignature + try { + val ms = MethodSignature.parseFrom(elem.signature().stringValue()) + mb.with(SignatureAttribute.of(remapMethodSignature(ms, mapFunction))) + } catch (e: Exception) { + e.printStackTrace() + // Fallback or rethrow? Usually parseFrom shouldn't fail on valid class. + // If it's not a MethodSignature (e.g. malformed), copy original + mb.with(elem) + } + } else { + mb.with(elem) + } + } + } + } + is Superclass -> { + // Remap superclass + val newSc = mapFunction.apply(element.superclassEntry().asSymbol()) + builder.withSuperclass(builder.constantPool().classEntry(newSc)) + } + is Interfaces -> { + val newInterfaces = ArrayList() + for (iface in element.interfaces()) { + newInterfaces.add( + builder.constantPool().classEntry(mapFunction.apply(iface.asSymbol())) + ) + } + builder.withInterfaces(newInterfaces) + } + is SignatureAttribute -> { + // Class Signature + try { + val cs = ClassSignature.parseFrom(element.signature().stringValue()) + builder.with(SignatureAttribute.of(remapClassSignature(cs, mapFunction))) + } catch (e: Exception) { + e.printStackTrace() + builder.with(element) + } + } + else -> { + builder.with(element) + } + } + } + } + + private class SimpleCodeRemapper( + private val mapFunction: Function, + private val mapValueFunction: Function, + ) : CodeTransform { + + override fun accept(builder: CodeBuilder, element: CodeElement) { + when (element) { + is TypeCheckInstruction -> { // CHECKCAST, INSTANCEOF + val newType = mapFunction.apply(element.type().asSymbol()) + if (element.opcode() == Opcode.CHECKCAST) { + builder.checkcast(newType) + } else if (element.opcode() == Opcode.INSTANCEOF) { + builder.instanceOf(newType) + } else { + builder.with(element) // fallback + } + } + is NewObjectInstruction -> { + builder.new_(mapFunction.apply(element.className().asSymbol())) + } + is NewReferenceArrayInstruction -> { + builder.anewarray(mapFunction.apply(element.componentType().asSymbol())) + } + is FieldInstruction -> { + val newOwner = mapFunction.apply(element.owner().asSymbol()) + val newType = mapFunction.apply(element.typeSymbol()) + val name = element.name().stringValue() + val op = element.opcode() + if (op == Opcode.GETFIELD) builder.getfield(newOwner, name, newType) + else if (op == Opcode.PUTFIELD) builder.putfield(newOwner, name, newType) + else if (op == Opcode.GETSTATIC) builder.getstatic(newOwner, name, newType) + else if (op == Opcode.PUTSTATIC) builder.putstatic(newOwner, name, newType) + else builder.with(element) + } + is InvokeInstruction -> { + val newOwner = mapFunction.apply(element.owner().asSymbol()) + val newDesc = mapMethodDesc(element.typeSymbol(), mapFunction) + val name = element.name().stringValue() + val isInterface = element.isInterface() + val op = element.opcode() + if (op == Opcode.INVOKEVIRTUAL) builder.invokevirtual(newOwner, name, newDesc) + else if (op == Opcode.INVOKESPECIAL) + builder.invokespecial(newOwner, name, newDesc, isInterface) + else if (op == Opcode.INVOKESTATIC) + builder.invokestatic(newOwner, name, newDesc, isInterface) + else if (op == Opcode.INVOKEINTERFACE) builder.invokeinterface(newOwner, name, newDesc) + else builder.with(element) + } + is ConstantInstruction.LoadConstantInstruction -> { + if (element.constantEntry() is ClassEntry) { + val ce = element.constantEntry() as ClassEntry + builder.ldc(mapFunction.apply(ce.asSymbol())) + } else if (element.constantEntry() is StringEntry) { + val se = element.constantEntry() as StringEntry + builder.ldc(mapValueFunction.apply(se.stringValue())) + } else { + builder.with(element) + } + } + else -> { + builder.with(element) + } + } + } + } + + private fun mapMethodDesc( + desc: MethodTypeDesc, + mapFunction: Function, + ): MethodTypeDesc { + val newReturnType = mapFunction.apply(desc.returnType()) + val newParamTypes = + desc.parameterList().stream().map(mapFunction).toArray { size -> + arrayOfNulls(size) + } + return MethodTypeDesc.of(newReturnType, *newParamTypes) + } + + private fun remapClassSignature( + cs: ClassSignature, + mapFunction: Function, + ): ClassSignature { + return ClassSignature.of( + remapTypeParams(cs.typeParameters(), mapFunction), // Expecting List + remapSignature(cs.superclassSignature(), mapFunction) as Signature.ClassTypeSig, + *cs + .superinterfaceSignatures() + .stream() + .map { s -> remapSignature(s, mapFunction) as Signature.ClassTypeSig } + .toArray { size -> arrayOfNulls(size) }, + ) + } + + private fun remapMethodSignature( + ms: MethodSignature, + mapFunction: Function, + ): MethodSignature { + return MethodSignature.of( + remapTypeParams(ms.typeParameters(), mapFunction), // Expecting List + ms + .throwableSignatures() + .stream() + .map { s -> remapSignature(s, mapFunction) as Signature.ThrowableSig } + .toList(), + remapSignature(ms.result(), mapFunction), + *ms + .arguments() + .stream() + .map { s -> remapSignature(s, mapFunction) } + .toArray { size -> arrayOfNulls(size) }, + ) + } + + private fun remapSignature( + sig: Signature, + mapFunction: Function, + ): Signature { + System.err.println("DEBUG: remapSignature " + sig.javaClass.simpleName) + if (sig is Signature.ClassTypeSig) { + val newDesc = mapFunction.apply(sig.classDesc()) + val newArgs = ArrayList() + for (arg in sig.typeArgs()) { + if (arg is Signature.TypeArg.Bounded) { + val newBound = remapSignature(arg.boundType(), mapFunction) as Signature.RefTypeSig + val indicator = arg.wildcardIndicator() + if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.NONE) { + newArgs.add(Signature.TypeArg.of(newBound)) + } else if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.EXTENDS) { + newArgs.add(Signature.TypeArg.extendsOf(newBound)) + } else if (indicator == Signature.TypeArg.Bounded.WildcardIndicator.SUPER) { + newArgs.add(Signature.TypeArg.superOf(newBound)) + } + } else if (arg is Signature.TypeArg.Unbounded) { + newArgs.add(arg) + } + } + val outer = sig.outerType().orElse(null) + val newOuter = + if (outer != null) remapSignature(outer, mapFunction) as Signature.ClassTypeSig else null + if (newOuter != null) { + return Signature.ClassTypeSig.of(newOuter, newDesc, *newArgs.toTypedArray()) + } else { + return Signature.ClassTypeSig.of(newDesc, *newArgs.toTypedArray()) + } + } else if (sig is Signature.ArrayTypeSig) { + return Signature.ArrayTypeSig.of(remapSignature(sig.componentSignature(), mapFunction)) + } else if (sig is Signature.TypeVarSig) { + return sig // Keep type var name + } else if (sig is Signature.BaseTypeSig) { + return sig + } + return sig + } + + private fun remapTypeParams( + params: List, + mapFunction: Function, + ): List { + val newParams = ArrayList() + for (p in params) { + val newClassBound = + if (p.classBound().isPresent) + remapSignature(p.classBound().get(), mapFunction) as Signature.RefTypeSig + else null + val newInterfaceBounds = + p.interfaceBounds() + .stream() + .map { s -> remapSignature(s, mapFunction) as Signature.RefTypeSig } + .toArray { size -> arrayOfNulls(size) } + newParams.add(Signature.TypeParam.of(p.identifier(), newClassBound, *newInterfaceBounds)) + } + return newParams + } +} From 3ae46d85c115920f7f86943d19cbf2fdf5ce1d85 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 15 Jan 2026 14:22:07 +0800 Subject: [PATCH 3/5] Fix compile --- .../gradle/plugins/shadow/internal/ClassFileHelper.kt | 3 ++- .../gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt index df135d36d..546e7fff4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ClassFileHelper.kt @@ -26,6 +26,7 @@ import java.lang.classfile.instruction.NewObjectInstruction import java.lang.classfile.instruction.NewReferenceArrayInstruction import java.lang.classfile.instruction.TypeCheckInstruction import java.lang.constant.ClassDesc +import java.lang.constant.ConstantDesc import java.lang.constant.MethodTypeDesc import java.util.function.Function @@ -181,7 +182,7 @@ internal object ClassFileHelper { builder.ldc(mapFunction.apply(ce.asSymbol())) } else if (element.constantEntry() is StringEntry) { val se = element.constantEntry() as StringEntry - builder.ldc(mapValueFunction.apply(se.stringValue())) + builder.ldc(mapValueFunction.apply(se.stringValue()) as ConstantDesc) } else { builder.with(element) } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt index 5cc83d4ea..9c7f3fed5 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import assertk.assertThat import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper +import java.lang.constant.ClassDesc import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -13,7 +14,7 @@ class RelocatorRemapperTest { fun relocateSignaturePatterns(input: String, expected: String) { val relocator = RelocatorRemapper(relocators = setOf(SimpleRelocator("org.package", "shadow.org.package"))) - assertThat(relocator.map(input)).isEqualTo(expected) + assertThat(relocator.map(ClassDesc.of(input))).isEqualTo(expected) } private companion object { From 01771a2eb1aa966f797b28eebe9448dde5348afb Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 15 Jan 2026 14:27:47 +0800 Subject: [PATCH 4/5] Fix toolchain --- build.gradle.kts | 4 +++- settings.gradle.kts | 5 +---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7526ef8da..cb22bec14 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,8 +26,9 @@ description = providers.gradleProperty("POM_DESCRIPTION").get() dokka { dokkaPublications.html { outputDirectory = rootDir.resolve("docs/api") } } +java { toolchain { languageVersion = JavaLanguageVersion.of(25) } } + kotlin { - jvmToolchain(libs.versions.jdkRelease.get().toInt()) explicitApi() @OptIn(ExperimentalAbiValidation::class) abiValidation { enabled = true } compilerOptions { @@ -37,6 +38,7 @@ kotlin { languageVersion = apiVersion jvmTarget = JvmTarget.fromTarget(libs.versions.jdkRelease.get()) jvmDefault = JvmDefaultMode.NO_COMPATIBILITY + freeCompilerArgs.add("-Xjdk-release=${libs.versions.jdkRelease.get()}") } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 34dd46553..93027b77d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,10 +12,7 @@ pluginManagement { } } -plugins { - id("com.gradle.develocity") version "4.3" - id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" -} +plugins { id("com.gradle.develocity") version "4.3" } develocity { buildScan { From 01935771d3dc9959761b9841b2c2e366f96945a1 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 15 Jan 2026 14:37:38 +0800 Subject: [PATCH 5/5] Revert RelocationTest --- .../jengelman/gradle/plugins/shadow/RelocationTest.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 25e4a991a..852ebcc79 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -32,8 +32,6 @@ import org.junit.jupiter.params.provider.ValueSource import org.opentest4j.AssertionFailedError class RelocationTest : BasePluginTest() { - private val javaExec = System.getProperty("java.home") + "/bin/java" - @ParameterizedTest @ValueSource(strings = ["foo", "new.pkg", "new/path"]) fun autoRelocation(relocationPrefix: String) { @@ -498,7 +496,7 @@ class RelocationTest : BasePluginTest() { ) runWithSuccess(shadowJarPath) - val result = runProcess(javaExec, "-jar", outputShadowedJar.use { it.toString() }) + val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) assertThat(result).contains("shadow.foo.Foo", "shadow.foo.Bar") } @@ -526,7 +524,7 @@ class RelocationTest : BasePluginTest() { ) runWithSuccess(shadowJarPath) - val result = runProcess(javaExec, "-jar", outputShadowedJar.use { it.toString() }) + val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) if (skipStringConstants) { assertThat(result).contains("foo.Foo", "foo.Bar") @@ -564,7 +562,7 @@ class RelocationTest : BasePluginTest() { ) runWithSuccess(shadowJarPath) - val result = runProcess(javaExec, "-jar", outputShadowedJar.use { it.toString() }) + val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) // Just check that the jar can be executed without NoClassDefFoundError. assertThat(result)