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