diff --git a/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapClassFilter.java b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapClassFilter.java new file mode 100644 index 00000000..f0c894f2 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapClassFilter.java @@ -0,0 +1,64 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * 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 net.fabricmc.loom.decompilers.linemap; + +import java.io.IOException; +import java.util.function.Predicate; + +import org.jetbrains.annotations.Nullable; + +/** + * A line map visitor that filters entries based on the class name. + * + *

If the {@code filter} returns false for class, its declaration and + * all contained line entries will be skipped. + * + * @author Juuz + */ +public final class LineMapClassFilter extends LineMapVisitor { + private final Predicate filter; + private boolean active = true; + + public LineMapClassFilter(@Nullable LineMapVisitor next, Predicate filter) { + super(next); + this.filter = filter; + } + + @Override + public void visitClass(String name, int max, int maxDest) throws IOException { + active = filter.test(name); + + if (active) { + super.visitClass(name, max, maxDest); + } + } + + @Override + public void visitLine(int src, int dest) throws IOException { + if (active) { + super.visitLine(src, dest); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapReader.java b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapReader.java new file mode 100644 index 00000000..2b9f5b03 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapReader.java @@ -0,0 +1,68 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * 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 net.fabricmc.loom.decompilers.linemap; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; + +/** + * A reader for line map files that in the format of {@link net.fabricmc.loom.decompilers.LineNumberRemapper}. + * {@linkplain #accept Accepts} a {@link LineMapVisitor} that processes the contents. + * + * @author Juuz + */ +public final class LineMapReader implements Closeable { + private final BufferedReader reader; + + public LineMapReader(BufferedReader reader) { + this.reader = reader; + } + + public void accept(LineMapVisitor visitor) throws IOException { + String line; + + while ((line = reader.readLine()) != null) { + if (line.isBlank()) continue; + + String[] parts = line.trim().split("\t"); + + try { + if (!line.startsWith("\t")) { + visitor.visitClass(parts[0], Integer.parseInt(parts[1]), Integer.parseInt(parts[2])); + } else { + visitor.visitLine(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); + } + } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { + throw new RuntimeException("Could not parse line: " + line, e); + } + } + } + + @Override + public void close() throws IOException { + reader.close(); + } +} diff --git a/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapVisitor.java b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapVisitor.java new file mode 100644 index 00000000..a32abc88 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapVisitor.java @@ -0,0 +1,92 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * 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 net.fabricmc.loom.decompilers.linemap; + +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.UnaryOperator; + +import org.jetbrains.annotations.Nullable; + +/** + * A visitor for line mapping files that can be read with {@link net.fabricmc.loom.decompilers.LineNumberRemapper}. + * + *

Delegates by default to a {@code next} visitor if it's not null, like ASM's {@code ClassVisitor}. + * + * @author Juuz + */ +public abstract class LineMapVisitor { + private final LineMapVisitor next; + + public LineMapVisitor(@Nullable LineMapVisitor next) { + this.next = next; + } + + /** + * Visits a class declaration. + * + * @param name the internal class name (e.g. {@code a/b/C$Nested}) + * @param max the original maximum line number in the class + * @param maxDest the new maximum line number in the class + */ + public void visitClass(String name, int max, int maxDest) throws IOException { + if (next != null) { + next.visitClass(name, max, maxDest); + } + } + + /** + * Visits a line in the {@linkplain #visitClass previously visited class}. + * + * @param src the original line number + * @param dest the mapped line number + */ + public void visitLine(int src, int dest) throws IOException { + if (next != null) { + next.visitLine(src, dest); + } + } + + /** + * Processes a line mapping file using an arbitrary visitor. + * + * @param path the path to the line mapping file + * @param visitorFactory a factory that creates a "filtering" line map visitor + */ + public static void process(Path path, UnaryOperator visitorFactory) throws IOException { + StringWriter sw = new StringWriter(); + LineMapWriter writer = new LineMapWriter(sw); + LineMapVisitor visitor = visitorFactory.apply(writer); + + try (LineMapReader reader = new LineMapReader(Files.newBufferedReader(path, StandardCharsets.UTF_8))) { + reader.accept(visitor); + } + + Files.writeString(path, sw.toString(), StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapWriter.java b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapWriter.java new file mode 100644 index 00000000..bb455e78 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/decompilers/linemap/LineMapWriter.java @@ -0,0 +1,63 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * 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 net.fabricmc.loom.decompilers.linemap; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Writer; + +/** + * A writer for line maps in the format of {@link net.fabricmc.loom.decompilers.LineNumberRemapper}. + * + * @author Juuz + */ +public final class LineMapWriter extends LineMapVisitor implements Closeable { + private final Writer writer; + + public LineMapWriter(Writer writer) { + super(null); + this.writer = writer; + } + + @Override + public void visitClass(String name, int max, int maxDest) throws IOException { + writer.append(name) + .append('\t').append(Integer.toString(max)) + .append('\t').append(Integer.toString(maxDest)) + .append('\n'); + } + + @Override + public void visitLine(int src, int dest) throws IOException { + writer.append('\t').append(Integer.toString(src)) + .append('\t').append(Integer.toString(dest)) + .append('\n'); + } + + @Override + public void close() throws IOException { + writer.close(); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 385821b4..78227e03 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -70,6 +70,8 @@ import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; import net.fabricmc.loom.configuration.processors.ModJavadocProcessor; import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper; import net.fabricmc.loom.decompilers.LineNumberRemapper; +import net.fabricmc.loom.decompilers.linemap.LineMapClassFilter; +import net.fabricmc.loom.decompilers.linemap.LineMapVisitor; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.IOStringConsumer; @@ -174,6 +176,9 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { } params.getClassPath().setFrom(getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES)); + + // Architectury + params.getForge().set(getExtension().isForge()); }); try { @@ -223,6 +228,9 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { RegularFileProperty getIPCPath(); ConfigurableFileCollection getClassPath(); + + // Architectury + Property getForge(); } public abstract static class DecompileAction implements WorkAction { @@ -287,6 +295,16 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { } if (Files.exists(linemap)) { + if (getParameters().getForge().get()) { + try { + // Remove Forge classes from linemap + // TODO: We should instead not decompile Forge's classes at all + LineMapVisitor.process(linemap, next -> new LineMapClassFilter(next, name -> !name.startsWith("net/minecraftforge/"))); + } catch (IOException e) { + throw new UncheckedIOException("Failed to process linemap", e); + } + } + try { // Line map the actually jar used to run the game, not the one used to decompile remapLineNumbers(metadata.logger(), runtimeJar, linemap, linemapJar); diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/LineMapTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/LineMapTest.groovy new file mode 100644 index 00000000..e137e087 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/LineMapTest.groovy @@ -0,0 +1,59 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * 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 net.fabricmc.loom.test.unit + +import net.fabricmc.loom.decompilers.linemap.LineMapClassFilter +import net.fabricmc.loom.decompilers.linemap.LineMapReader +import net.fabricmc.loom.decompilers.linemap.LineMapWriter +import spock.lang.Specification + +class LineMapTest extends Specification { + LineMapReader reader = new LineMapReader(LineMapTest.getResourceAsStream('/linemap/input.lmap').newReader('UTF-8')) + + private String readExpected(String name) { + return LineMapTest.getResourceAsStream("/linemap/${name}.lmap") + .getText('UTF-8') + .replace('\r\n', '\n') + } + + def "roundtrip"() { + when: + def sw = new StringWriter() + reader.accept(new LineMapWriter(sw)) + reader.close() + then: + sw.toString() == readExpected('simpleOutput') + } + + def "filter"() { + when: + def sw = new StringWriter() + def writer = new LineMapWriter(sw) + reader.accept(new LineMapClassFilter(writer, { it.startsWith('test/nested/') })) + reader.close() + then: + sw.toString() == readExpected('filteredOutput') + } +} diff --git a/src/test/resources/linemap/filteredOutput.lmap b/src/test/resources/linemap/filteredOutput.lmap new file mode 100644 index 00000000..eaf3be42 --- /dev/null +++ b/src/test/resources/linemap/filteredOutput.lmap @@ -0,0 +1,4 @@ +test/nested/C 3 5 + 2 3 + 4 5 +test/nested/D 0 0 diff --git a/src/test/resources/linemap/input.lmap b/src/test/resources/linemap/input.lmap new file mode 100644 index 00000000..546947df --- /dev/null +++ b/src/test/resources/linemap/input.lmap @@ -0,0 +1,11 @@ +test/A 3 4 + 1 2 + +test/B 1 1 + 1 1 + +test/nested/C 3 5 + 2 3 + 4 5 + +test/nested/D 0 0 diff --git a/src/test/resources/linemap/simpleOutput.lmap b/src/test/resources/linemap/simpleOutput.lmap new file mode 100644 index 00000000..4995545b --- /dev/null +++ b/src/test/resources/linemap/simpleOutput.lmap @@ -0,0 +1,8 @@ +test/A 3 4 + 1 2 +test/B 1 1 + 1 1 +test/nested/C 3 5 + 2 3 + 4 5 +test/nested/D 0 0