diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 91cfb9c4..90dd4ab1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,6 @@ version = "1.0" accessWidener = "2.1.0" asm = "9.7.1" checker = "3.48.4" -forgeAutoRenamingTool = "1.0.8" vineFlower = "1.10.1" ideaExt = "1.3" immutables = "2.10.1" @@ -26,7 +25,6 @@ accessWidener = { module = "net.fabricmc:access-widener", version.ref = "accessW asm = { module = "org.ow2.asm:asm", version.ref = "asm" } asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" } asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" } -forgeAutoRenamingTool = { module = "net.minecraftforge:ForgeAutoRenamingTool", version.ref = "forgeAutoRenamingTool" } vineFlower = { module = "org.vineflower:vineflower", version.ref = "vineFlower" } gson = { module = "com.google.code.gson:gson", version = "2.11.0" } ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" } diff --git a/subprojects/gradle-plugin/build.gradle.kts b/subprojects/gradle-plugin/build.gradle.kts index d07b8e1f..1538b24c 100644 --- a/subprojects/gradle-plugin/build.gradle.kts +++ b/subprojects/gradle-plugin/build.gradle.kts @@ -20,21 +20,18 @@ val accessWiden by sourceSets.creating { val shadow by sourceSets.creating { configurations.named(this.implementationConfigurationName) { extendsFrom(commonDeps) } } +val transformer by sourceSets.creating { + configurations.named(this.implementationConfigurationName) { extendsFrom(commonDeps) } +} configurations { api { extendsFrom(commonDeps) } - "jarDecompileCompileClasspath" { - attributes { - attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 11) // VF needs 11 - } - } } val accessWidenerVersion: String by project val asmVersion: String by project val checkerVersion: String by project val vineFlowerVersion: String by project -val forgeAutoRenamingToolVersion: String by project val junitVersion: String by project val mergeToolVersion: String by project dependencies { @@ -43,10 +40,6 @@ dependencies { commonDeps(libs.asm) commonDeps(libs.asm.commons) commonDeps(libs.asm.util) - commonDeps(libs.forgeAutoRenamingTool) { - exclude("org.ow2.asm") // Use our own ASM - exclude("net.sf.jopt-simple") - } commonDeps(libs.mammoth) // Just main @@ -77,11 +70,14 @@ dependencies { "accessWidenCompileOnly"(libs.accessWidener) { exclude("org.ow2.asm") } + "accessWidenCompileOnly"(transformer.output) implementation(accessWiden.output) "shadowCompileOnly"(libs.shadowPlugin) implementation(shadow.output) + implementation(transformer.output) + testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.api) testRuntimeOnly(libs.junit.launcher) diff --git a/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerEntryTransformer.java b/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerEntryTransformer.java index 72f9c6f4..b1077849 100644 --- a/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerEntryTransformer.java +++ b/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerEntryTransformer.java @@ -26,14 +26,10 @@ import net.fabricmc.accesswidener.AccessWidener; import net.fabricmc.accesswidener.AccessWidenerClassVisitor; -import net.fabricmc.accesswidener.AccessWidenerVisitor; -import net.minecraftforge.fart.api.Transformer; -import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; +import org.spongepowered.gradle.vanilla.internal.transformer.ClassTransformer; -final class AccessWidenerEntryTransformer implements Transformer { +final class AccessWidenerEntryTransformer implements ClassTransformer { private final AccessWidener widener; public AccessWidenerEntryTransformer(final AccessWidener widener) { @@ -41,18 +37,7 @@ public AccessWidenerEntryTransformer(final AccessWidener widener) { } @Override - public ClassEntry process(final ClassEntry entry) { - // Because InnerClass attributes can be present in any class AW'd classes - // are referenced from, we have to target every class to get a correct output. - final ClassReader reader = new ClassReader(entry.getData()); - final ClassWriter writer = new ClassWriter(reader, 0); - // TODO: Expose the ASM version constant somewhere visible to this worker - final ClassVisitor visitor = AccessWidenerClassVisitor.createClassVisitor(Opcodes.ASM9, writer, this.widener); - reader.accept(visitor, 0); - if (entry.isMultiRelease()) { - return ClassEntry.create(entry.getName(), entry.getTime(), writer.toByteArray(), entry.getVersion()); - } else { - return ClassEntry.create(entry.getName(), entry.getTime(), writer.toByteArray()); - } + public ClassVisitor apply(final ClassVisitor parent) { + return AccessWidenerClassVisitor.createClassVisitor(ClassTransformer.ASM_VERSION, parent, this.widener); } } diff --git a/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerTransformerProvider.java b/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerTransformerProvider.java index 39a840e3..53a53fe3 100644 --- a/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerTransformerProvider.java +++ b/subprojects/gradle-plugin/src/accessWiden/java/org/spongepowered/gradle/vanilla/internal/worker/AccessWidenerTransformerProvider.java @@ -26,10 +26,10 @@ import net.fabricmc.accesswidener.AccessWidener; import net.fabricmc.accesswidener.AccessWidenerReader; -import net.minecraftforge.fart.api.Transformer; import org.gradle.api.GradleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.spongepowered.gradle.vanilla.internal.transformer.ClassTransformer; import java.io.BufferedReader; import java.io.IOException; @@ -39,12 +39,12 @@ import java.util.Set; import java.util.function.Function; -public final class AccessWidenerTransformerProvider implements Function, Transformer> { +public final class AccessWidenerTransformerProvider implements Function, ClassTransformer> { private static final Logger LOGGER = LoggerFactory.getLogger(AccessWidenerTransformerProvider.class); @Override - public Transformer apply(final Set paths) { + public ClassTransformer apply(final Set paths) { final AccessWidener widener = new AccessWidener(); final AccessWidenerReader reader = new AccessWidenerReader(widener); diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/Constants.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/Constants.java index 542ecbac..0dfaf274 100644 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/Constants.java +++ b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/Constants.java @@ -48,7 +48,6 @@ public final class Constants { public static final String USER_AGENT = String.format("%s/%s Gradle/%s", Constants.NAME, Constants.VERSION, GradleVersion.current().getVersion()); public static final String MINECRAFT_RESOURCES_HOST = "resources.download.minecraft.net"; public static final String TASK_GROUP = "vanilla gradle"; - public static final int ASM_VERSION = Opcodes.ASM9; public static final String FIRST_TARGETABLE_RELEASE_TIMESTAMP = "2019-09-04T11:19:34+00:00"; // 19w36a+ public static final String OUT_OF_BAND_RELEASE = "1.14.4"; // Cause it is special public static final String INDENT = " "; // indent to use when writing files diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/LocalVariableNamer.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/LocalVariableNamer.java deleted file mode 100644 index 79b0e560..00000000 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/LocalVariableNamer.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of VanillaGradle, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.gradle.vanilla.internal.asm; - -import org.objectweb.asm.Handle; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.spongepowered.gradle.vanilla.internal.Constants; - -public final class LocalVariableNamer extends MethodVisitor { - - private final boolean isStatic; - private final int paramTotal; - private int parameterCount = 0; - private int lvtCount; - private final VariableScopeTracker scopeTracker; - private final VariableScope scope; - - public LocalVariableNamer( - final boolean isStatic, - final int paramTotal, - final VariableScopeTracker scopeTracker, - final VariableScope scope, - final MethodVisitor methodVisitor - ) { - super(Constants.ASM_VERSION, methodVisitor); - this.isStatic = isStatic; - this.paramTotal = paramTotal; - this.scopeTracker = scopeTracker; - this.scope = scope; - } - - @Override - public void visitParameter(final String name, final int access) { - // Normalize parameter attributes - if (name == null) { - super.visitParameter(null, access); - return; - } - - if (LocalVariableNamer.isValidJavaIdentifier(name)) { - super.visitParameter(this.scope.produceSafe(name, VariableScope.Usage.PARAMETER), access); - } else { - super.visitParameter(this.scope.produceSafe("param" + this.parameterCount, VariableScope.Usage.PARAMETER), access); - } - - this.parameterCount++; - } - - @Override - public void visitInvokeDynamicInsn( - final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments - ) { - for (final Object arg : bootstrapMethodArguments) { - if (arg instanceof Handle) { - final Handle referenced = (Handle) arg; - if (referenced.getOwner().equals(this.scopeTracker.className())) { // a reference to a method within ourselves, assume nested scope - this.scopeTracker.makeChild(this.scope, referenced.getName(), referenced.getDesc()); - } - } - } - super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); - } - - @Override - public void visitLocalVariable(final String name, final String descriptor, final String signature, final Label start, final Label end, - final int index) { - final int varNumber = this.lvtCount++; - final int paramLocalVarSplit = this.isStatic ? this.paramTotal : this.paramTotal + 1; - final String varName; - if (LocalVariableNamer.isValidJavaIdentifier(name)) { - varName = name; - } else if (varNumber == 0 && !this.isStatic) { - varName = "this"; - } else if (varNumber < paramLocalVarSplit) { - varName = "param" + (varNumber - (this.isStatic ? 0 : 1)); - } else { - varName = "var" + (varNumber - paramLocalVarSplit); - } - - super.visitLocalVariable(this.scope.produceSafe(varName, VariableScope.Usage.LVT), descriptor, signature, start, end, index); - } - - private static boolean isValidJavaIdentifier(final String name) { - if (name.isEmpty()) { - return false; - } - if (!Character.isJavaIdentifierStart(name.charAt(0))) { - return false; - } - - for (int i = 1; i < name.length(); i++) { - if (!Character.isJavaIdentifierPart(name.charAt(i))) { - return false; - } - } - - return true; - } -} diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/LocalVariableNamingClassVisitor.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/LocalVariableNamingClassVisitor.java deleted file mode 100644 index 865559d0..00000000 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/LocalVariableNamingClassVisitor.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of VanillaGradle, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.gradle.vanilla.internal.asm; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.spongepowered.gradle.vanilla.internal.Constants; - -public final class LocalVariableNamingClassVisitor extends ClassVisitor { - - private final VariableScopeTracker tracker = new VariableScopeTracker(); - - public LocalVariableNamingClassVisitor(final ClassVisitor classVisitor) { - super(Constants.ASM_VERSION, classVisitor); - } - - @Override - public void visit( - final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces - ) { - super.visit(version, access, name, signature, superName, interfaces); - this.tracker.className(name); - } - - @Override - public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { - int paramVarCount = 0; - try { - final Type[] md = Type.getMethodType(descriptor).getArgumentTypes(); - paramVarCount = md.length; - } catch (final Exception ignored) { - // oh well, let's be tolerant of bad bytecode - } - return new LocalVariableNamer( - (access & Opcodes.ACC_STATIC) != 0, - paramVarCount, - this.tracker, - this.tracker.scope(name, descriptor), - super.visitMethod(access, name, descriptor, signature, exceptions) - ); - } -} diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/VariableScope.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/VariableScope.java deleted file mode 100644 index c919757a..00000000 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/VariableScope.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This file is part of VanillaGradle, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.gradle.vanilla.internal.asm; - -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.util.HashMap; -import java.util.Map; - -/** - * Rudimentary scope model to prevent var name collisions between lambda methods and their containing methods - */ -final class VariableScope { - - private static final String THIS = "this"; - - private final Map usedNames = new HashMap<>(); - private final @Nullable VariableScope parent; - - static VariableScope root() { - return new VariableScope(null); - } - - VariableScope(final @Nullable VariableScope parent) { - this.parent = parent; - } - - // Scope-by-scope for ourselves, but don't care for parents - boolean used(final String variable, final Usage usage) { - if (usage == Usage.LVT && variable.equals(VariableScope.THIS)) { - return false; - } else if (this.usedNames.getOrDefault(variable, UsageHolder.NONE).has(usage)) { - return true; - } else if (this.parent == null) { - return false; - } - - VariableScope scope = this.parent; - - while (!scope.usedNames.containsKey(variable)) { - if (scope.parent == null) { - return false; - } - scope = scope.parent; - } - - return true; - } - - String produceSafe(final String variable, final Usage usage) { - if (!this.used(variable, usage)) { - this.usedNames.compute(variable, (k, existing) -> (existing == null ? UsageHolder.NONE : existing).plus(usage)); - return variable; - } - - final StringBuilder resultBuilder = new StringBuilder(variable.length() + 1); - resultBuilder.append(variable); - String result; - do { - resultBuilder.append("x"); - result = resultBuilder.toString(); - } while (this.used(result, usage)); - - this.usedNames.compute(variable, (k, existing) -> (existing == null ? UsageHolder.NONE : existing).plus(usage)); - return result; - } - - static class UsageHolder { - - static final UsageHolder NONE = new UsageHolder(false, false); - static final UsageHolder PARAM = new UsageHolder(false, true); - static final UsageHolder LVT = new UsageHolder(true, false); - static final UsageHolder BOTH = new UsageHolder(true, true); - - final boolean lvt; - final boolean param; - - private UsageHolder(final boolean lvt, final boolean param) { - this.param = param; - this.lvt = lvt; - } - - UsageHolder plus(final Usage usage) { - switch (usage) { - case LVT: return this.param ? UsageHolder.BOTH : UsageHolder.LVT; - case PARAMETER: return this.lvt ? UsageHolder.BOTH : UsageHolder.PARAM; - } - return this; - } - - boolean any() { - return this.lvt || this.param; - } - - boolean has(final Usage usage) { - switch (usage) { - case LVT: return this.lvt; - case PARAMETER: return this.param; - default: throw new IllegalArgumentException("Unknown " + usage); - } - } - } - - enum Usage { - LVT, - PARAMETER, - } - -} diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/VariableScopeTracker.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/VariableScopeTracker.java deleted file mode 100644 index 57913118..00000000 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/asm/VariableScopeTracker.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of VanillaGradle, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.gradle.vanilla.internal.asm; - -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -import java.util.HashMap; -import java.util.Map; - -final class VariableScopeTracker { - private final Map trackedScopes = new HashMap<>(); - private @MonotonicNonNull String className; - - public VariableScope scope(final String methodName, final String desc) { - return this.trackedScopes.computeIfAbsent(VariableScopeTracker.toMapKey(methodName, desc), $ -> VariableScope.root()); - } - - public void makeChild(final VariableScope parent, final String childName, final String childDesc) { - final VariableScope childScope = new VariableScope(parent); - this.trackedScopes.putIfAbsent(VariableScopeTracker.toMapKey(childName, childDesc), childScope); - } - - private static String toMapKey(final String methodName, final String methodDesc) { - return methodName + methodDesc; - } - - String className() { - return this.className; - } - - void className(final String name) { - this.className = name; - } -} diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/AccessWidenerModifier.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/AccessWidenerModifier.java index aaacfb44..ada37a4c 100644 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/AccessWidenerModifier.java +++ b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/AccessWidenerModifier.java @@ -24,11 +24,11 @@ */ package org.spongepowered.gradle.vanilla.internal.repository.modifier; -import net.minecraftforge.fart.api.Transformer; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.gradle.vanilla.internal.repository.ResolvableTool; import org.spongepowered.gradle.vanilla.internal.resolver.AsyncUtils; +import org.spongepowered.gradle.vanilla.internal.transformer.ClassTransformer; import org.spongepowered.gradle.vanilla.repository.MinecraftResolver; import org.spongepowered.gradle.vanilla.resolver.HashAlgorithm; @@ -86,13 +86,13 @@ public String stateKey() { @Override @SuppressWarnings("unchecked") - public CompletableFuture providePopulator( + public CompletableFuture providePopulator( final MinecraftResolver.Context context ) { final Supplier loaderProvider = context.classLoaderWithTool(ResolvableTool.ACCESS_WIDENER); - return AsyncUtils.failableFuture(() -> new TransformerProvider() { + return AsyncUtils.failableFuture(() -> new ClassTransformer.Provider() { private final URLClassLoader loader = loaderProvider.get(); - private @Nullable Function, Transformer> accessWidenerLoader = (Function, Transformer>) Class.forName( + private @Nullable Function, ClassTransformer> accessWidenerLoader = (Function, ClassTransformer>) Class.forName( "org.spongepowered.gradle.vanilla.internal.worker.AccessWidenerTransformerProvider", true, this.loader @@ -101,7 +101,7 @@ public CompletableFuture providePopulator( .newInstance(); @Override - public Transformer provide() { + public ClassTransformer provide() { if (this.accessWidenerLoader == null) { throw new IllegalStateException("Already closed!"); } diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/ArtifactModifier.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/ArtifactModifier.java index cb41ffc1..45a48697 100644 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/ArtifactModifier.java +++ b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/modifier/ArtifactModifier.java @@ -24,16 +24,14 @@ */ package org.spongepowered.gradle.vanilla.internal.repository.modifier; -import net.minecraftforge.fart.api.Renamer; -import net.minecraftforge.fart.api.Transformer; +import org.spongepowered.gradle.vanilla.internal.transformer.ClassTransformer; import org.spongepowered.gradle.vanilla.repository.MinecraftResolver; -import java.io.IOException; import java.util.Set; import java.util.concurrent.CompletableFuture; /** - * Some sort of operation that can be performed on a jar, via a {@link Renamer}. + * Some sort of operation that can be performed on a jar, via a {@link ClassTransformer.Provider}. */ public interface ArtifactModifier { @@ -86,7 +84,7 @@ static String decorateArtifactId(final String originalId, final Set providePopulator(final MinecraftResolver.Context context); + CompletableFuture providePopulator(final MinecraftResolver.Context context); /** * Indicates that the result of this modification should be stored in the @@ -96,16 +94,4 @@ static String decorateArtifactId(final String originalId, final Set - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.gradle.vanilla.internal.transformer; - -import net.minecraftforge.fart.api.Transformer; - -import java.util.Set; - -final class FilterClassesTransformer implements Transformer { - - private final String[] allowedPrefixes; - - FilterClassesTransformer(final Set allowedPrefixes) { - this.allowedPrefixes = allowedPrefixes.toArray(new String[0]); - } - - @Override - public ClassEntry process(final ClassEntry entry) { - if (this.matches(entry.getName())) { - return entry; - } else { - return null; - } - } - - private boolean matches(final String className) { - if (!className.contains("/")) { - return true; - } - - for (final String pkg : this.allowedPrefixes) { - if (className.startsWith(pkg)) { - return true; - } - } - return false; - - } - - @Override - public ResourceEntry process(final ResourceEntry entry) { - return entry; - /*if (!this.matches(entry.getConfig().getService().replace('.', '/'))) { - return null; - } - - final List providers = new ArrayList<>(entry.getConfig().getProviders().size()); - for (final String provider : entry.getConfig().getProviders()) { - if (this.matches(provider.replace('.', '/'))) { - providers.add(provider); - } - } - if (providers.isEmpty()) { - return null; - } - - return new JarServiceProviderConfigurationEntry(entry.getTime(), new ServiceProviderConfiguration(entry.getConfig().getService(), providers));*/ - } -} diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/LocalVariableNameFixer.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/LocalVariableNameFixer.java deleted file mode 100644 index 93448c43..00000000 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/LocalVariableNameFixer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of VanillaGradle, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.gradle.vanilla.internal.transformer; - -import net.minecraftforge.fart.api.Transformer; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.spongepowered.gradle.vanilla.internal.asm.LocalVariableNamingClassVisitor; - -final class LocalVariableNameFixer implements Transformer { - - @Override - public ClassEntry process(final ClassEntry entry) { - final ClassReader reader = new ClassReader(entry.getData()); - final ClassWriter writer = new ClassWriter(reader, 0); - final LocalVariableNamingClassVisitor visitor = new LocalVariableNamingClassVisitor(writer); - reader.accept(visitor, 0); - return ClassEntry.create(entry.getName(), entry.getTime(), writer.toByteArray()); - } - -} diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/RecordSignatureFixer.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/RecordSignatureFixer.java deleted file mode 100644 index 7f7328ab..00000000 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/RecordSignatureFixer.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * This file is part of VanillaGradle, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.gradle.vanilla.internal.transformer; - -import net.minecraftforge.fart.api.ClassProvider; -import net.minecraftforge.fart.api.Transformer; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.RecordComponentVisitor; -import org.objectweb.asm.Type; -import org.objectweb.asm.signature.SignatureReader; -import org.objectweb.asm.signature.SignatureVisitor; -import org.objectweb.asm.signature.SignatureWriter; -import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.gradle.vanilla.internal.Constants; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; - -public class RecordSignatureFixer implements Transformer { - private final Consumer debug; - private final ClassProvider inh; - - public RecordSignatureFixer(final Consumer debug, final ClassProvider inh) { - this.debug = debug; - this.inh = inh; - } - - @Override - public ClassEntry process(final ClassEntry entry) { - final ClassReader reader = new ClassReader(entry.getData()); - final ClassWriter writer = new ClassWriter(reader, 0); - - reader.accept(new Fixer(writer), 0); - return ClassEntry.create(entry.getName(), entry.getTime(), writer.toByteArray()); - } - - private class Fixer extends ClassVisitor { - private TypeParameterCollector paramCollector; - private boolean isRecord; - private boolean patchSignature; - private final ClassVisitor originalParent; - private ClassNode node; - - public Fixer(final ClassVisitor parent) { - super(Constants.ASM_VERSION, parent); - this.originalParent = parent; - } - - @Override - public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { - this.isRecord = "java/lang/Record".equals(superName); - this.patchSignature = signature == null; - if (this.isRecord && this.patchSignature) { - this.node = new ClassNode(); - this.cv = this.node; - } - // todo: validate type parameters from superinterfaces - // this would need to get signature information from bytecode + runtime classes - super.visit(version, access, name, signature, superName, interfaces); - } - - @Override - public RecordComponentVisitor visitRecordComponent(final String name, final String descriptor, final String signature) { - if (signature != null && this.patchSignature) { // signature implies non-primitive type - if (this.paramCollector == null) { - this.paramCollector = new TypeParameterCollector(); - } - this.paramCollector.baseType = Type.getType(descriptor); - this.paramCollector.param = TypeParameterCollector.FIELD; - new SignatureReader(signature).accept(this.paramCollector); - } - return super.visitRecordComponent(name, descriptor, signature); - } - - @Override - public FieldVisitor visitField(final int access, final String name, final String descriptor, final String signature, final Object value) { - if (this.isRecord && signature != null && this.patchSignature) { // signature implies non-primitive type - if (this.paramCollector == null) - this.paramCollector = new TypeParameterCollector(); - this.paramCollector.baseType = Type.getType(descriptor); - this.paramCollector.param = TypeParameterCollector.FIELD; - new SignatureReader(signature).accept(this.paramCollector); - } - return super.visitField(access, name, descriptor, signature, value); - } - - @Override - public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { - if ((access & Opcodes.ACC_STATIC) == 0 && this.isRecord && signature != null && this.patchSignature) { // signature implies non-primitive type - if (this.paramCollector == null) - this.paramCollector = new TypeParameterCollector(); - this.paramCollector.baseType = Type.getType(descriptor); - this.paramCollector.param = TypeParameterCollector.FIELD; // start out before parameters come in - new SignatureReader(signature).accept(this.paramCollector); - if (this.paramCollector.declaredParams != null) { - this.paramCollector.declaredParams.clear(); - } - } - return super.visitMethod(access, name, descriptor, signature, exceptions); - } - - @Override - public void visitEnd() { - if (this.isRecord && this.patchSignature && this.paramCollector != null && !this.paramCollector.typeParameters.isEmpty()) { - // Proguard also strips the Signature attribute, so we have to reconstruct that, to a point where this class is accepted by - // javac when on the classpath. This requires every type parameter referenced to have been declared within the class. - // Records are implicitly static and have a defined superclass of java/lang/Record, so there can be type parameters in play from: - // - fields - // - methods (which can declare their own formal parameters) - // - record components - // - superinterfaces (less important, we just get raw type warnings) - // - // This will not be perfect, but provides enough information to allow compilation and enhance decompiler output. - // todo: allow type-specific rules to infer deeper levels (for example, T with raw type Comparable is probably Comparable) - - final SignatureWriter sw = new SignatureWriter(); - // Formal parameters - // find all used type parameters, plus guesstimated bounds - for (final Map.Entry param : this.paramCollector.typeParameters.entrySet()) { - sw.visitFormalTypeParameter(param.getKey()); - if (!param.getValue().equals(TypeParameterCollector.UNKNOWN)) { - final ClassProvider.IClassInfo cls = RecordSignatureFixer.this.inh.getClass(param.getValue()).orElse(null); - if (cls != null) { - final SignatureVisitor parent; - if ((cls.getAccess() & Opcodes.ACC_INTERFACE) != 0) { - parent = sw.visitInterfaceBound(); - } else { - parent = sw.visitClassBound(); - } - parent.visitClassType(param.getValue()); - parent.visitEnd(); - continue; - } else { - RecordSignatureFixer.this.debug.accept("Unable to find information for type " + param.getValue()); - } - } - final SignatureVisitor cls = sw.visitClassBound(); - cls.visitClassType("java/lang/Object"); - cls.visitEnd(); - } - - // Supertype (always Record) - final SignatureVisitor sv = sw.visitSuperclass(); - sv.visitClassType(this.node.superName); - sv.visitEnd(); - - // Superinterfaces - for (final String superI : this.node.interfaces) { - final SignatureVisitor itfV = sw.visitInterface(); - itfV.visitClassType(superI); - sv.visitEnd(); - } - final String newSignature = sw.toString(); - RecordSignatureFixer.this.debug.accept("New signature for " + this.node.name + ": " + newSignature); - this.node.signature = newSignature; - } - // feed node through to the original output visitor - if (this.node != null && this.originalParent != null) { - this.node.accept(this.originalParent); - } - } - } - - static class TypeParameterCollector extends SignatureVisitor { - private static final int RETURN_TYPE = -2; - static final int FIELD = -1; - static final String UNKNOWN = "???"; - Map typeParameters = new HashMap<>(); // - Type baseType; - int param = -1; - int level; - Set declaredParams; - - public TypeParameterCollector() { - super(Constants.ASM_VERSION); - } - - @Override - public void visitFormalTypeParameter(final String name) { - if (this.declaredParams == null) - this.declaredParams = new HashSet<>(); - this.declaredParams.add(name); - } - - @Override - public void visitTypeVariable(final String name) { - if ((!this.typeParameters.containsKey(name) || this.typeParameters.get(name).equals(TypeParameterCollector.UNKNOWN)) && (this.declaredParams == null || !this.declaredParams.contains(name))) { - if (this.level == 0 && this.baseType != null) { - final String typeName; - switch (this.param) { - case TypeParameterCollector.FIELD: // field - typeName = this.baseType.getInternalName(); - break; - case TypeParameterCollector.RETURN_TYPE: // method return value - typeName = this.baseType.getReturnType().getInternalName(); - break; - default: - typeName = this.baseType.getArgumentTypes()[this.param].getInternalName(); - break; - } - this.typeParameters.put(name, typeName); - } else { - this.typeParameters.put(name, TypeParameterCollector.UNKNOWN); - } - } - super.visitTypeVariable(name); - } - - @Override - public void visitClassType(final String name) { - this.level++; - super.visitClassType(name); - } - - @Override - public void visitInnerClassType(final String name) { - this.level++; - super.visitInnerClassType(name); - } - - @Override - public SignatureVisitor visitTypeArgument(final char wildcard) { - this.level++; - return super.visitTypeArgument(wildcard); - } - - @Override - public void visitEnd() { - if (this.level-- <= 0) { - throw new IllegalStateException("Unbalanced signature levels"); - } - super.visitEnd(); - } - - // for methods - - @Override - public SignatureVisitor visitParameterType() { - this.param++; - return super.visitParameterType(); - } - - @Override - public SignatureVisitor visitReturnType() { - this.param = TypeParameterCollector.RETURN_TYPE; - return super.visitReturnType(); - } - } - -} diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftResolverImpl.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftResolverImpl.java index 2882fff2..042a5137 100644 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftResolverImpl.java +++ b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftResolverImpl.java @@ -24,13 +24,7 @@ */ package org.spongepowered.gradle.vanilla.repository; -import net.minecraftforge.fart.api.Renamer; -import net.minecraftforge.fart.api.SignatureStripperConfig; -import net.minecraftforge.fart.api.SourceFixerConfig; -import net.minecraftforge.fart.api.Transformer; -import net.minecraftforge.srgutils.IMappingFile; import org.checkerframework.checker.nullness.qual.Nullable; -import org.gradle.api.GradleException; import org.immutables.value.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +40,8 @@ import org.spongepowered.gradle.vanilla.internal.repository.modifier.AssociatedResolutionFlags; import org.spongepowered.gradle.vanilla.internal.resolver.AsyncUtils; import org.spongepowered.gradle.vanilla.internal.resolver.FileUtils; -import org.spongepowered.gradle.vanilla.internal.transformer.Transformers; +import org.spongepowered.gradle.vanilla.internal.transformer.ClassTransformer; +import org.spongepowered.gradle.vanilla.internal.transformer.JarTransformer; import org.spongepowered.gradle.vanilla.internal.util.FunctionalUtils; import org.spongepowered.gradle.vanilla.internal.util.SelfPreferringClassLoader; import org.spongepowered.gradle.vanilla.resolver.Downloader; @@ -54,7 +49,6 @@ import org.spongepowered.gradle.vanilla.resolver.ResolutionResult; import java.io.IOException; -import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -149,7 +143,7 @@ public Supplier classLoaderWithTool(final ResolvableTool tool) { return AsyncUtils.memoizedSupplier(() -> new SelfPreferringClassLoader(classPath, MinecraftResolverImpl.class.getClassLoader())); } - // remap a single-sided jar + // provide a single-sided jar CompletableFuture> provide(final MinecraftPlatform platform, final MinecraftSide side, final String version, final Path outputJar) { return this.artifacts.computeIfAbsent(EnvironmentKey.of(platform, version, null), key -> { final CompletableFuture> descriptorFuture = this.manifests.fullVersion(key.versionId()); @@ -159,27 +153,18 @@ CompletableFuture> provide(final Minecraf } final VersionDescriptor.Full descriptor = potentialDescriptor.get(); final Download jarDownload = descriptor.requireDownload(side.executableArtifact()); - final Download mappingsDownload = descriptor.requireDownload(side.mappingsArtifact()); // download to temp path - final String tempJarPath = this.sharedArtifactFileName(platform.artifactId() + "_m-obf_b-bundled", version, null, "jar"); - final String jarPath = this.sharedArtifactFileName(platform.artifactId() + "_m-obf", version, null, "jar"); - final String mappingsPath = this.sharedArtifactFileName(platform.artifactId() + "_m-obf", version, "mappings", "txt"); + final String bundledJarPath = this.sharedArtifactFileName(platform.artifactId() + "_b-bundled", version, null, "jar"); final CompletableFuture> jarFuture = this.downloader.downloadAndValidate( jarDownload.url(), - tempJarPath, + bundledJarPath, HashAlgorithm.SHA1, jarDownload.sha1() ); - final CompletableFuture> mappingsFuture = this.downloader.downloadAndValidate( - mappingsDownload.url(), - mappingsPath, - HashAlgorithm.SHA1, - mappingsDownload.sha1() - ); - return jarFuture.thenCombineAsync(mappingsFuture, (jar, mappingsFile) -> { + return jarFuture.thenApplyAsync((jar) -> { try { final boolean outputExists = Files.exists(outputJar); final @Nullable BundlerMetadata bundlerMeta = BundlerMetadata.read(jar.get()).orElse(null); @@ -189,7 +174,7 @@ CompletableFuture> provide(final Minecraf MinecraftResolverImpl.LOGGER.info("No bundler metadata found in jar {}", jar.get()); } final Supplier> dependencies = () -> side.dependencies(descriptor, bundlerMeta); - if (!this.forceRefresh && jar.upToDate() && mappingsFile.upToDate() && outputExists) { + if (!this.forceRefresh && jar.upToDate() && outputExists) { // Our inputs are up-to-date, and the output exists, so we can assume (for now) that the output is up-to-date // Check meta here too, before returning this.writeMetaIfNecessary(platform, potentialDescriptor, dependencies, outputJar.getParent()); @@ -198,9 +183,6 @@ CompletableFuture> provide(final Minecraf } else if (!jar.isPresent()) { throw new IllegalArgumentException("No jar was available for Minecraft " + descriptor.id() + "side " + side.name() + "! Are you sure the data file is correct?"); - } else if (!mappingsFile.isPresent()) { - throw new IllegalArgumentException("No mappings were available for Minecraft " + descriptor.id() + "side " + side.name() - + "! Official mappings are only available for releases 1.14.4 and newer."); } MinecraftResolverImpl.LOGGER.warn("Preparing Minecraft: Java Edition {} version {}", side, version); this.cleanAssociatedArtifacts(platform, version); @@ -209,38 +191,7 @@ CompletableFuture> provide(final Minecraf FileUtils.createDirectoriesSymlinkSafe(outputJar.getParent()); // Extract jar - final Path extracted = this.downloader.baseDir().resolve(jarPath); - side.extractJar(jar.get(), extracted, bundlerMeta); - - final IMappingFile scratchMappings; - try ( - final InputStream reader = Files.newInputStream(mappingsFile.get()) - ) { - scratchMappings = IMappingFile.load(reader); - } catch (final IOException ex) { - throw new GradleException("Failed to read mappings from " + mappingsFile, ex); - } - final IMappingFile mappings = scratchMappings.reverse(); - - final Renamer.Builder renamerBuilder = Renamer.builder(); - - if (bundlerMeta == null && !side.allowedPackages().isEmpty()) { - renamerBuilder.add(ctx -> Transformers.filterEntries(side.allowedPackages())); - } - renamerBuilder.add(Transformer.parameterAnnotationFixerFactory()) - .add(Transformers.fixLvNames()) - .add(Transformer.renamerFactory(mappings, true)) - .add(Transformer.sourceFixerFactory(SourceFixerConfig.JAVA)) - .add(Transformer.recordFixerFactory()) - .add(Transformer.signatureStripperFactory(SignatureStripperConfig.ALL)) - .add(Transformers.recordSignatureFixer()); // for versions where old PG produced invalid record signatures - - renamerBuilder.logger(MinecraftResolverImpl.LOGGER::info); - // todo: threads - // todo: dependencies - try (final Renamer ren = renamerBuilder.build()) { - ren.run(extracted.toFile(), outputTmp.toFile()); - } + side.extractJar(jar.get(), outputTmp, bundlerMeta); this.writeMetaIfNecessary(platform, potentialDescriptor, dependencies, outputJar.getParent()); FileUtils.atomicMove(outputTmp, outputJar); @@ -386,7 +337,7 @@ public CompletableFuture> provide( boolean requiresLocalStorage = false; // Synchronously compute the modifier populator providers @SuppressWarnings({"unchecked", "rawtypes"}) - final CompletableFuture[] populators = new CompletableFuture[modifiers.size()]; + final CompletableFuture[] populators = new CompletableFuture[modifiers.size()]; int idx = 0; for (final ArtifactModifier modifier : modifiers) { @@ -418,16 +369,12 @@ public CompletableFuture> provide( final Path outputTmp = Files.createTempDirectory("vanillagradle").resolve("output" + decoratedArtifact + ".jar"); FileUtils.createDirectoriesSymlinkSafe(output.getParent()); - final Renamer.Builder builder = Renamer.builder() - .logger(MinecraftResolverImpl.LOGGER::info); - - for (final CompletableFuture populator : populators) { + final JarTransformer.Builder builder = JarTransformer.builder(); + for (final CompletableFuture populator : populators) { builder.add(populator.get().provide()); } - try (final Renamer ren = builder.build()) { - ren.run(input.get().jar().toFile(), outputTmp.toFile()); - } + builder.build().transform(input.get().jar(), outputTmp); FileUtils.atomicMove(outputTmp, output); this.writeMetaIfNecessary(side, decoratedArtifact, input.mapIfPresent((upToDate, env) -> env.metadata()), input.get()::dependencies, output.getParent()); @@ -436,7 +383,7 @@ public CompletableFuture> provide( } catch (final Exception ex) { throw new CompletionException(ex); } finally { - for (final CompletableFuture populator : populators) { + for (final CompletableFuture populator : populators) { if (!populator.isCompletedExceptionally()) { try { populator.join().close(); diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftSide.java b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftSide.java index d6ba6856..0671985e 100644 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftSide.java +++ b/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftSide.java @@ -46,7 +46,7 @@ import java.util.stream.Collectors; public enum MinecraftSide { - CLIENT(DownloadClassifier.CLIENT, DownloadClassifier.CLIENT_MAPPINGS) { + CLIENT(DownloadClassifier.CLIENT) { @Override public Set dependencies( final VersionDescriptor.Full descriptor, @@ -56,15 +56,7 @@ public Set dependencies( return MinecraftSide.manifestLibraries(descriptor, RuleContext.create(), lib -> !lib.isNatives()); } }, - SERVER(DownloadClassifier.SERVER, DownloadClassifier.SERVER_MAPPINGS) { - private final Set packages; - - { - final Set allowedPackages = new HashSet<>(); - allowedPackages.add("net/minecraft"); - this.packages = Collections.unmodifiableSet(allowedPackages); - } - + SERVER(DownloadClassifier.SERVER) { @Override public Set dependencies( final VersionDescriptor.Full descriptor, @@ -103,30 +95,18 @@ public void extractJar( } } } - - @Override - public Set allowedPackages() { - // Filter this out if there is bundler metadata - return this.packages; - } }; private final DownloadClassifier executableArtifact; - private final DownloadClassifier mappingsArtifact; - MinecraftSide(final DownloadClassifier executableArtifact, final DownloadClassifier mappingsArtifact) { + MinecraftSide(final DownloadClassifier executableArtifact) { this.executableArtifact = executableArtifact; - this.mappingsArtifact = mappingsArtifact; } public final DownloadClassifier executableArtifact() { return this.executableArtifact; } - public final DownloadClassifier mappingsArtifact() { - return this.mappingsArtifact; - } - public abstract Set dependencies( final VersionDescriptor.Full descriptor, final @Nullable BundlerMetadata bundleMetadata diff --git a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/Transformers.java b/subprojects/gradle-plugin/src/transformer/java/org/spongepowered/gradle/vanilla/internal/transformer/ClassTransformer.java similarity index 67% rename from subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/Transformers.java rename to subprojects/gradle-plugin/src/transformer/java/org/spongepowered/gradle/vanilla/internal/transformer/ClassTransformer.java index 5c16b273..ea652ace 100644 --- a/subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/Transformers.java +++ b/subprojects/gradle-plugin/src/transformer/java/org/spongepowered/gradle/vanilla/internal/transformer/ClassTransformer.java @@ -24,25 +24,27 @@ */ package org.spongepowered.gradle.vanilla.internal.transformer; -import net.minecraftforge.fart.api.Transformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; -import java.util.Set; +import java.io.IOException; +import java.util.function.UnaryOperator; -public final class Transformers { - - private Transformers() { - } - - public static Transformer filterEntries(final Set allowedPackages) { - return new FilterClassesTransformer(allowedPackages); - } - - public static Transformer fixLvNames() { - return new LocalVariableNameFixer(); - } - - public static Transformer.Factory recordSignatureFixer() { - return ctx -> new RecordSignatureFixer(ctx.getDebug(), ctx.getClassProvider()); +/** + * Transforms the content of a class using a {@link ClassVisitor}. + */ +public interface ClassTransformer extends UnaryOperator { + int ASM_VERSION = Opcodes.ASM9; + + /** + * A function that can provide a {@link ClassTransformer}, optionally having a post-rename operation to clean up resources. + */ + @FunctionalInterface + interface Provider extends AutoCloseable { + ClassTransformer provide(); + + @Override + default void close() throws IOException { + } } - } diff --git a/subprojects/gradle-plugin/src/transformer/java/org/spongepowered/gradle/vanilla/internal/transformer/JarTransformer.java b/subprojects/gradle-plugin/src/transformer/java/org/spongepowered/gradle/vanilla/internal/transformer/JarTransformer.java new file mode 100644 index 00000000..eb8083ba --- /dev/null +++ b/subprojects/gradle-plugin/src/transformer/java/org/spongepowered/gradle/vanilla/internal/transformer/JarTransformer.java @@ -0,0 +1,150 @@ +/* + * This file is part of VanillaGradle, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.gradle.vanilla.internal.transformer; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +/** + * Transforms the content of a jar. + */ +public class JarTransformer { + private final ClassTransformer[] transformers; + + private JarTransformer(final ClassTransformer[] transformers) { + this.transformers = transformers; + } + + /** + * Transforms the content of the given jar. + * + * @param inputJar The input jar. + * @param outputJar The output jar. + * @throws IOException if an error occurs while reading or writing the jars. + */ + public void transform(final Path inputJar, final Path outputJar) throws IOException { + try (final ZipInputStream zipIn = new ZipInputStream(Files.newInputStream(inputJar)); + final ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(outputJar))) { + + ZipEntry entry; + while ((entry = zipIn.getNextEntry()) != null) { + zipOut.putNextEntry(entry); + + final String name = entry.getName(); + if (name.endsWith(".class")) { + this.transformClass(zipIn, zipOut); + } else if (name.equals("META-INF/MANIFEST.MF")) { + this.transformManifest(zipIn, zipOut); + } else { + zipIn.transferTo(zipOut); + } + + zipOut.closeEntry(); + } + } + } + + /** + * Applies class transformers. + * + * @param in The input stream. + * @param out The output stream. + * @throws IOException if an error occurs while reading or writing the entry. + */ + private void transformClass(final InputStream in, final OutputStream out) throws IOException { + final ClassReader reader = new ClassReader(in.readAllBytes()); + final ClassWriter writer = new ClassWriter(reader, 0); + ClassVisitor visitor = writer; + for (final ClassTransformer transformer : this.transformers) { + visitor = transformer.apply(visitor); + } + reader.accept(visitor, 0); + out.write(writer.toByteArray()); + } + + /** + * Strips all class signatures. + * + * @param in The input stream. + * @param out The output stream. + * @throws IOException if an error occurs while reading or writing the entry. + */ + private void transformManifest(final InputStream in, final OutputStream out) throws IOException { + final Manifest manifest = new Manifest(in); + manifest.getEntries().entrySet().removeIf((entry) -> { + final String name = entry.getKey(); + if (!name.endsWith(".class")) { + return false; + } + + final Attributes attributes = entry.getValue(); + attributes.entrySet().removeIf((attribute) -> { + final String key = attribute.getKey().toString().toLowerCase(Locale.ROOT); + return key.endsWith("-digest"); + }); + return attributes.isEmpty(); + }); + manifest.write(out); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private final List transformers = new ArrayList<>(); + + private Builder() {} + + public Builder add(final ClassTransformer transformer) { + this.transformers.add(Objects.requireNonNull(transformer, "transformer")); + return this; + } + + public Builder add(final ClassTransformer.Provider provider) { + return this.add(Objects.requireNonNull(provider, "provider").provide()); + } + + public JarTransformer build() { + return new JarTransformer(this.transformers.toArray(new ClassTransformer[0])); + } + } +}