aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/net/fabricmc/loom/decompilers
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/net/fabricmc/loom/decompilers')
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java6
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/LineNumberRemapper.java7
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/cfr/CFRObfuscationMapping.java230
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/cfr/CFRSinkFactory.java151
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/cfr/FabricCFRDecompiler.java233
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/cfr/LoomCFRDecompiler.java148
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java163
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractForkedFFExecutor.java107
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricFernFlowerDecompiler.java39
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/fernflower/FernflowerLogger.java (renamed from src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricForkedFFExecutor.java)64
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java70
-rw-r--r--src/main/java/net/fabricmc/loom/decompilers/fernflower/ThreadIDFFLogger.java127
12 files changed, 611 insertions, 734 deletions
diff --git a/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java b/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java
index bd51c4e8..0c58e5c8 100644
--- a/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java
+++ b/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java
@@ -27,7 +27,7 @@ package net.fabricmc.loom.decompilers;
import org.gradle.api.Project;
import net.fabricmc.loom.LoomGradleExtension;
-import net.fabricmc.loom.decompilers.cfr.FabricCFRDecompiler;
+import net.fabricmc.loom.decompilers.cfr.LoomCFRDecompiler;
import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler;
public final class DecompilerConfiguration {
@@ -36,7 +36,7 @@ public final class DecompilerConfiguration {
public static void setup(Project project) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
- extension.getGameDecompilers().add(new FabricFernFlowerDecompiler(project));
- extension.getGameDecompilers().add(new FabricCFRDecompiler(project));
+ extension.getGameDecompilers().add(new FabricFernFlowerDecompiler());
+ extension.getGameDecompilers().add(new LoomCFRDecompiler());
}
}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/LineNumberRemapper.java b/src/main/java/net/fabricmc/loom/decompilers/LineNumberRemapper.java
index 7e500301..307a340e 100644
--- a/src/main/java/net/fabricmc/loom/decompilers/LineNumberRemapper.java
+++ b/src/main/java/net/fabricmc/loom/decompilers/LineNumberRemapper.java
@@ -47,10 +47,9 @@ import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import net.fabricmc.loom.util.Constants;
-import net.fabricmc.loom.util.gradle.ProgressLogger;
+import net.fabricmc.loom.util.IOStringConsumer;
/**
- * TODO, Move to stitch.
* Created by covers1624 on 18/02/19.
*/
public class LineNumberRemapper {
@@ -88,7 +87,7 @@ public class LineNumberRemapper {
}
}
- public void process(ProgressLogger logger, Path input, Path output) throws IOException {
+ public void process(IOStringConsumer logger, Path input, Path output) throws IOException {
Files.walkFileTree(input, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
@@ -110,7 +109,7 @@ public class LineNumberRemapper {
String idx = rel.substring(0, rel.length() - 6);
if (logger != null) {
- logger.progress("Remapping " + idx);
+ logger.accept("Remapping " + idx);
}
int dollarPos = idx.indexOf('$'); //This makes the assumption that only Java classes are to be remapped.
diff --git a/src/main/java/net/fabricmc/loom/decompilers/cfr/CFRObfuscationMapping.java b/src/main/java/net/fabricmc/loom/decompilers/cfr/CFRObfuscationMapping.java
new file mode 100644
index 00000000..83afc21b
--- /dev/null
+++ b/src/main/java/net/fabricmc/loom/decompilers/cfr/CFRObfuscationMapping.java
@@ -0,0 +1,230 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2021 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.cfr;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.benf.cfr.reader.bytecode.analysis.types.JavaRefTypeInstance;
+import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance;
+import org.benf.cfr.reader.bytecode.analysis.types.MethodPrototype;
+import org.benf.cfr.reader.entities.AccessFlag;
+import org.benf.cfr.reader.entities.ClassFile;
+import org.benf.cfr.reader.entities.ClassFileField;
+import org.benf.cfr.reader.entities.Field;
+import org.benf.cfr.reader.mapping.NullMapping;
+import org.benf.cfr.reader.util.output.DelegatingDumper;
+import org.benf.cfr.reader.util.output.Dumper;
+
+import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
+import net.fabricmc.mappingio.MappingReader;
+import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
+import net.fabricmc.mappingio.tree.MappingTree;
+import net.fabricmc.mappingio.tree.MemoryMappingTree;
+
+public class CFRObfuscationMapping extends NullMapping {
+ private final MappingTree mappingTree;
+
+ public CFRObfuscationMapping(Path mappings) {
+ mappingTree = readMappings(mappings);
+ }
+
+ @Override
+ public Dumper wrap(Dumper d) {
+ return new JavadocProvidingDumper(d);
+ }
+
+ private static MappingTree readMappings(Path input) {
+ try (BufferedReader reader = Files.newBufferedReader(input)) {
+ MemoryMappingTree mappingTree = new MemoryMappingTree();
+ MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(mappingTree, MappingsNamespace.NAMED.toString());
+ MappingReader.read(reader, nsSwitch);
+
+ return mappingTree;
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read mappings", e);
+ }
+ }
+
+ private class JavadocProvidingDumper extends DelegatingDumper {
+ JavadocProvidingDumper(Dumper delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public Dumper dumpClassDoc(JavaTypeInstance owner) {
+ MappingTree.ClassMapping mapping = getClassMapping(owner);
+
+ if (mapping == null) {
+ return this;
+ }
+
+ List<String> recordComponentDocs = new LinkedList<>();
+
+ if (isRecord(owner)) {
+ ClassFile classFile = ((JavaRefTypeInstance) owner).getClassFile();
+
+ for (ClassFileField field : classFile.getFields()) {
+ if (field.getField().testAccessFlag(AccessFlag.ACC_STATIC)) {
+ continue;
+ }
+
+ MappingTree.FieldMapping fieldMapping = mapping.getField(field.getFieldName(), field.getField().getDescriptor());
+
+ if (fieldMapping == null) {
+ continue;
+ }
+
+ String comment = fieldMapping.getComment();
+
+ if (comment != null) {
+ recordComponentDocs.add(String.format("@param %s %s", fieldMapping.getSrcName(), comment));
+ }
+ }
+ }
+
+ String comment = mapping.getComment();
+
+ if (comment != null || !recordComponentDocs.isEmpty()) {
+ print("/**").newln();
+
+ if (comment != null) {
+ for (String line : comment.split("\\R")) {
+ print(" * ").print(line).newln();
+ }
+
+ if (!recordComponentDocs.isEmpty()) {
+ print(" * ").newln();
+ }
+ }
+
+ for (String componentDoc : recordComponentDocs) {
+ print(" * ").print(componentDoc).newln();
+ }
+
+ print(" */").newln();
+ }
+
+ return this;
+ }
+
+ @Override
+ public Dumper dumpMethodDoc(MethodPrototype method) {
+ MappingTree.ClassMapping classMapping = getClassMapping(method.getOwner());
+
+ if (classMapping == null) {
+ return this;
+ }
+
+ List<String> lines = new ArrayList<>();
+ MappingTree.MethodMapping mapping = classMapping.getMethod(method.getName(), method.getOriginalDescriptor());
+
+ if (mapping != null) {
+ String comment = mapping.getComment();
+
+ if (comment != null) {
+ lines.addAll(Arrays.asList(comment.split("\\R")));
+ }
+
+ for (MappingTree.MethodArgMapping arg : mapping.getArgs()) {
+ String argComment = arg.getComment();
+
+ if (argComment != null) {
+ lines.addAll(Arrays.asList(("@param " + arg.getSrcName() + " " + argComment).split("\\R")));
+ }
+ }
+ }
+
+ if (!lines.isEmpty()) {
+ print("/**").newln();
+
+ for (String line : lines) {
+ print(" * ").print(line).newln();
+ }
+
+ print(" */").newln();
+ }
+
+ return this;
+ }
+
+ @Override
+ public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) {
+ // None static fields in records are handled in the class javadoc.
+ if (isRecord(owner) && !isStatic(field)) {
+ return null;
+ }
+
+ MappingTree.ClassMapping classMapping = getClassMapping(owner);
+
+ if (classMapping == null) {
+ return null;
+ }
+
+ MappingTree.FieldMapping fieldMapping = classMapping.getField(field.getFieldName(), field.getDescriptor());
+ dumpComment(fieldMapping.getComment());
+
+ return this;
+ }
+
+ private MappingTree.ClassMapping getClassMapping(JavaTypeInstance type) {
+ String qualifiedName = type.getRawName().replace('.', '/');
+ return mappingTree.getClass(qualifiedName);
+ }
+
+ private boolean isRecord(JavaTypeInstance javaTypeInstance) {
+ if (javaTypeInstance instanceof JavaRefTypeInstance) {
+ ClassFile classFile = ((JavaRefTypeInstance) javaTypeInstance).getClassFile();
+ return classFile.getClassSignature().getSuperClass().getRawName().equals("java.lang.Record");
+ }
+
+ return false;
+ }
+
+ private boolean isStatic(Field field) {
+ return field.testAccessFlag(AccessFlag.ACC_STATIC);
+ }
+
+ private void dumpComment(String comment) {
+ if (comment == null || comment.isBlank()) {
+ return;
+ }
+
+ print("/**").newln();
+
+ for (String line : comment.split("\n")) {
+ print(" * ").print(line).newln();
+ }
+
+ print(" */").newln();
+ }
+ }
+}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/cfr/CFRSinkFactory.java b/src/main/java/net/fabricmc/loom/decompilers/cfr/CFRSinkFactory.java
new file mode 100644
index 00000000..bdc5a28a
--- /dev/null
+++ b/src/main/java/net/fabricmc/loom/decompilers/cfr/CFRSinkFactory.java
@@ -0,0 +1,151 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2021 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.cfr;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+import com.google.common.base.Charsets;
+import org.benf.cfr.reader.api.OutputSinkFactory;
+import org.benf.cfr.reader.api.SinkReturns;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.fabricmc.loom.util.IOStringConsumer;
+
+public class CFRSinkFactory implements OutputSinkFactory {
+ private static final Logger ERROR_LOGGER = LoggerFactory.getLogger(CFRSinkFactory.class);
+
+ private final JarOutputStream outputStream;
+ private final IOStringConsumer logger;
+ private final Set<String> addedDirectories = new HashSet<>();
+ private final Map<String, Map<Integer, Integer>> lineMap = new TreeMap<>();
+
+ public CFRSinkFactory(JarOutputStream outputStream, IOStringConsumer logger) {
+ this.outputStream = outputStream;
+ this.logger = logger;
+ }
+
+ @Override
+ public List<SinkClass> getSupportedSinks(SinkType sinkType, Collection<SinkClass> available) {
+ return switch (sinkType) {
+ case JAVA -> Collections.singletonList(SinkClass.DECOMPILED);
+ case LINENUMBER -> Collections.singletonList(SinkClass.LINE_NUMBER_MAPPING);
+ default -> Collections.emptyList();
+ };
+ }
+
+ @Override
+ public <T> Sink<T> getSink(SinkType sinkType, SinkClass sinkClass) {
+ return switch (sinkType) {
+ case JAVA -> (Sink<T>) decompiledSink();
+ case LINENUMBER -> (Sink<T>) lineNumberMappingSink();
+ case EXCEPTION -> (e) -> ERROR_LOGGER.error((String) e);
+ default -> null;
+ };
+ }
+
+ private Sink<SinkReturns.Decompiled> decompiledSink() {
+ return sinkable -> {
+ String filename = sinkable.getPackageName().replace('.', '/');
+ if (!filename.isEmpty()) filename += "/";
+ filename += sinkable.getClassName() + ".java";
+
+ byte[] data = sinkable.getJava().getBytes(Charsets.UTF_8);
+
+ writeToJar(filename, data);
+ };
+ }
+
+ private Sink<SinkReturns.LineNumberMapping> lineNumberMappingSink() {
+ return sinkable -> {
+ final String className = sinkable.getClassName();
+ final NavigableMap<Integer, Integer> classFileMappings = sinkable.getClassFileMappings();
+ final NavigableMap<Integer, Integer> mappings = sinkable.getMappings();
+
+ if (classFileMappings == null || mappings == null) return;
+
+ for (Map.Entry<Integer, Integer> entry : mappings.entrySet()) {
+ // New line number
+ Integer dstLineNumber = entry.getValue();
+
+ // Line mapping in the original jar
+ Integer srcLineNumber = classFileMappings.get(entry.getKey());
+
+ if (srcLineNumber == null || dstLineNumber == null) continue;
+
+ lineMap.computeIfAbsent(className, (c) -> new TreeMap<>()).put(srcLineNumber, dstLineNumber);
+ }
+ };
+ }
+
+ private synchronized void writeToJar(String filename, byte[] data) {
+ String[] path = filename.split("/");
+ String pathPart = "";
+
+ for (int i = 0; i < path.length - 1; i++) {
+ pathPart += path[i] + "/";
+
+ if (addedDirectories.add(pathPart)) {
+ JarEntry entry = new JarEntry(pathPart);
+ entry.setTime(new Date().getTime());
+
+ try {
+ outputStream.putNextEntry(entry);
+ outputStream.closeEntry();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ JarEntry entry = new JarEntry(filename);
+ entry.setTime(new Date().getTime());
+ entry.setSize(data.length);
+
+ try {
+ logger.accept("Writing: " + filename);
+ outputStream.putNextEntry(entry);
+ outputStream.write(data);
+ outputStream.closeEntry();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Map<String, Map<Integer, Integer>> getLineMap() {
+ return Collections.unmodifiableMap(lineMap);
+ }
+}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/cfr/FabricCFRDecompiler.java b/src/main/java/net/fabricmc/loom/decompilers/cfr/FabricCFRDecompiler.java
deleted file mode 100644
index 7237a499..00000000
--- a/src/main/java/net/fabricmc/loom/decompilers/cfr/FabricCFRDecompiler.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * This file is part of fabric-loom, licensed under the MIT License (MIT).
- *
- * Copyright (c) 2020-2021 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.cfr;
-
-import java.io.FileNotFoundException;
-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.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.function.Function;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableMap;
-import org.benf.cfr.reader.api.CfrDriver;
-import org.benf.cfr.reader.api.ClassFileSource;
-import org.benf.cfr.reader.api.OutputSinkFactory;
-import org.benf.cfr.reader.api.SinkReturns;
-import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
-import org.gradle.api.Project;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.internal.logging.progress.ProgressLogger;
-import org.gradle.internal.logging.progress.ProgressLoggerFactory;
-import org.gradle.internal.service.ServiceRegistry;
-
-import net.fabricmc.loom.api.decompilers.DecompilationMetadata;
-import net.fabricmc.loom.api.decompilers.LoomDecompiler;
-
-public class FabricCFRDecompiler implements LoomDecompiler {
- private final Project project;
-
- public FabricCFRDecompiler(Project project) {
- this.project = project;
- }
-
- @Override
- public String name() {
- return "ExperimentalCfr";
- }
-
- @Override
- public void decompile(Path compiledJar, Path sourcesDestination, Path linemapDestination, DecompilationMetadata metaData) {
- project.getLogger().warn("!!!! The CFR decompiler support is currently incomplete, line numbers will not match up and there will be no javadocs in the generated source.");
-
- // Setups the multi threaded logger, the thread id is used as the key to the ProgressLogger's
- ServiceRegistry registry = ((ProjectInternal) project).getServices();
- ProgressLoggerFactory factory = registry.get(ProgressLoggerFactory.class);
- ProgressLogger progressGroup = factory.newOperation(getClass()).setDescription("Decompile");
-
- Map<Long, ProgressLogger> loggerMap = new ConcurrentHashMap<>();
- Function<Long, ProgressLogger> createLogger = (threadId) -> {
- ProgressLogger pl = factory.newOperation(getClass(), progressGroup);
- pl.setDescription("decompile worker");
- pl.started();
- return pl;
- };
-
- progressGroup.started();
-
- Manifest manifest = new Manifest();
- manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
- Set<String> addedDirectories = new HashSet<>();
-
- try (OutputStream fos = Files.newOutputStream(sourcesDestination); JarOutputStream jos = new JarOutputStream(fos, manifest); ZipFile inputZip = new ZipFile(compiledJar.toFile())) {
- CfrDriver driver = new CfrDriver.Builder()
- .withOptions(ImmutableMap.of(
- "renameillegalidents", "true",
- "trackbytecodeloc", "true"
- ))
- .withClassFileSource(new ClassFileSource() {
- @Override
- public void informAnalysisRelativePathDetail(String usePath, String classFilePath) {
- }
-
- @Override
- public Collection<String> addJar(String jarPath) {
- return null;
- }
-
- @Override
- public String getPossiblyRenamedPath(String path) {
- return path;
- }
-
- @Override
- public Pair<byte[], String> getClassFileContent(String path) throws IOException {
- ZipEntry zipEntry = inputZip.getEntry(path);
-
- if (zipEntry == null) {
- throw new FileNotFoundException(path);
- }
-
- try (InputStream inputStream = inputZip.getInputStream(zipEntry)) {
- return Pair.make(inputStream.readAllBytes(), path);
- }
- }
- })
- .withOutputSink(new OutputSinkFactory() {
- @Override
- public List<SinkClass> getSupportedSinks(SinkType sinkType, Collection<SinkClass> available) {
- return switch (sinkType) {
- case PROGRESS -> Collections.singletonList(SinkClass.STRING);
- case JAVA -> Collections.singletonList(SinkClass.DECOMPILED);
- default -> Collections.emptyList();
- };
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <T> Sink<T> getSink(SinkType sinkType, SinkClass sinkClass) {
- return switch (sinkType) {
- case PROGRESS -> (p) -> project.getLogger().debug((String) p);
- case JAVA -> (Sink<T>) decompiledSink(jos, addedDirectories);
- case EXCEPTION -> (e) -> project.getLogger().error((String) e);
- default -> null;
- };
- }
- })
- .build();
-
- List<String> classes = Collections.list(inputZip.entries()).stream()
- .map(ZipEntry::getName)
- .filter(input -> input.endsWith(".class"))
- .collect(Collectors.toList());
-
- ExecutorService executorService = Executors.newFixedThreadPool(metaData.numberOfThreads());
- List<Future<?>> futures = new LinkedList<>();
-
- for (String clazz : classes) {
- futures.add(executorService.submit(() -> {
- loggerMap.computeIfAbsent(Thread.currentThread().getId(), createLogger).progress(clazz);
- driver.analyse(Collections.singletonList(clazz));
- }));
- }
-
- for (Future<?> future : futures) {
- future.get();
- }
- } catch (IOException | InterruptedException | ExecutionException e) {
- throw new RuntimeException("Failed to decompile", e);
- } finally {
- loggerMap.forEach((threadId, progressLogger) -> progressLogger.completed());
- }
- }
-
- private static OutputSinkFactory.Sink<SinkReturns.Decompiled> decompiledSink(JarOutputStream jos, Set<String> addedDirectories) {
- return decompiled -> {
- String filename = decompiled.getPackageName().replace('.', '/');
- if (!filename.isEmpty()) filename += "/";
- filename += decompiled.getClassName() + ".java";
-
- byte[] data = decompiled.getJava().getBytes(Charsets.UTF_8);
-
- writeToJar(filename, data, jos, addedDirectories);
- };
- }
-
- // TODO move to task queue?
- private static synchronized void writeToJar(String filename, byte[] data, JarOutputStream jos, Set<String> addedDirectories) {
- String[] path = filename.split("/");
- String pathPart = "";
-
- for (int i = 0; i < path.length - 1; i++) {
- pathPart += path[i] + "/";
-
- if (addedDirectories.add(pathPart)) {
- JarEntry entry = new JarEntry(pathPart);
- entry.setTime(new Date().getTime());
-
- try {
- jos.putNextEntry(entry);
- jos.closeEntry();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- JarEntry entry = new JarEntry(filename);
- entry.setTime(new Date().getTime());
- entry.setSize(data.length);
-
- try {
- jos.putNextEntry(entry);
- jos.write(data);
- jos.closeEntry();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/cfr/LoomCFRDecompiler.java b/src/main/java/net/fabricmc/loom/decompilers/cfr/LoomCFRDecompiler.java
new file mode 100644
index 00000000..bbfb0be2
--- /dev/null
+++ b/src/main/java/net/fabricmc/loom/decompilers/cfr/LoomCFRDecompiler.java
@@ -0,0 +1,148 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2021 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.cfr;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import org.benf.cfr.reader.Driver;
+import org.benf.cfr.reader.state.ClassFileSourceImpl;
+import org.benf.cfr.reader.state.DCCommonState;
+import org.benf.cfr.reader.util.AnalysisType;
+import org.benf.cfr.reader.util.getopt.Options;
+import org.benf.cfr.reader.util.getopt.OptionsImpl;
+import org.benf.cfr.reader.util.output.SinkDumperFactory;
+
+import net.fabricmc.loom.api.decompilers.DecompilationMetadata;
+import net.fabricmc.loom.api.decompilers.LoomDecompiler;
+import net.fabricmc.loom.decompilers.LineNumberRemapper;
+
+public class LoomCFRDecompiler implements LoomDecompiler {
+ private static final Map<String, String> DECOMPILE_OPTIONS = Map.of(
+ "renameillegalidents", "true",
+ "trackbytecodeloc", "true",
+ "comments", "false"
+ );
+
+ @Override
+ public String name() {
+ return "Cfr";
+ }
+
+ @Override
+ public void decompile(Path compiledJar, Path sourcesDestination, Path linemapDestination, DecompilationMetadata metaData) {
+ final String path = compiledJar.toAbsolutePath().toString();
+ final Options options = OptionsImpl.getFactory().create(DECOMPILE_OPTIONS);
+
+ ClassFileSourceImpl classFileSource = new ClassFileSourceImpl(options);
+
+ for (Path library : metaData.libraries()) {
+ classFileSource.addJarContent(library.toAbsolutePath().toString(), AnalysisType.JAR);
+ }
+
+ classFileSource.informAnalysisRelativePathDetail(null, null);
+
+ DCCommonState state = new DCCommonState(options, classFileSource);
+
+ if (metaData.javaDocs() != null) {
+ state = new DCCommonState(state, new CFRObfuscationMapping(metaData.javaDocs()));
+ }
+
+ final Manifest manifest = new Manifest();
+ manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+
+ Map<String, Map<Integer, Integer>> lineMap;
+
+ try (JarOutputStream outputStream = new JarOutputStream(Files.newOutputStream(sourcesDestination), manifest)) {
+ CFRSinkFactory cfrSinkFactory = new CFRSinkFactory(outputStream, metaData.logger());
+ SinkDumperFactory dumperFactory = new SinkDumperFactory(cfrSinkFactory, options);
+
+ Driver.doJar(state, path, AnalysisType.JAR, dumperFactory);
+
+ lineMap = cfrSinkFactory.getLineMap();
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to decompile", e);
+ }
+
+ writeLineMap(linemapDestination, lineMap);
+ }
+
+ private void writeLineMap(Path output, Map<String, Map<Integer, Integer>> lineMap) {
+ try (Writer writer = Files.newBufferedWriter(output, StandardCharsets.UTF_8)) {
+ for (Map.Entry<String, Map<Integer, Integer>> classEntry : lineMap.entrySet()) {
+ final String name = classEntry.getKey().replace(".", "/");
+
+ final Map<Integer, Integer> mapping = classEntry.getValue();
+
+ int maxLine = 0;
+ int maxLineDest = 0;
+ StringBuilder builder = new StringBuilder();
+
+ for (Map.Entry<Integer, Integer> mappingEntry : mapping.entrySet()) {
+ final int src = mappingEntry.getKey();
+ final int dst = mappingEntry.getValue();
+
+ maxLine = Math.max(maxLine, src);
+ maxLineDest = Math.max(maxLineDest, dst);
+
+ builder.append("\t").append(src).append("\t").append(dst).append("\n");
+ }
+
+ writer.write("%s\t%d\t%d\n".formatted(name, maxLine, maxLineDest));
+ writer.write(builder.toString());
+ writer.write("\n");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to write line map", e);
+ }
+ }
+
+ // A test main class to make it quicker/easier to debug with minimal jars
+ public static void main(String[] args) throws IOException {
+ LoomCFRDecompiler decompiler = new LoomCFRDecompiler();
+
+ Path lineMap = Paths.get("linemap.txt");
+
+ decompiler.decompile(Paths.get("input.jar"),
+ Paths.get("output-sources.jar"),
+ lineMap,
+ new DecompilationMetadata(4, null, Collections.emptyList(), null)
+ );
+
+ LineNumberRemapper lineNumberRemapper = new LineNumberRemapper();
+ lineNumberRemapper.readMappings(lineMap.toFile());
+ lineNumberRemapper.process(null, Paths.get("input.jar"), Paths.get("output.jar"));
+ }
+}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java
deleted file mode 100644
index f059ed36..00000000
--- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * This file is part of fabric-loom, licensed under the MIT License (MIT).
- *
- * Copyright (c) 2019-2021 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.fernflower;
-
-import static java.text.MessageFormat.format;
-
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Stack;
-import java.util.function.Supplier;
-
-import org.gradle.api.Project;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.internal.logging.progress.ProgressLogger;
-import org.gradle.internal.logging.progress.ProgressLoggerFactory;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.process.ExecResult;
-import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
-
-import net.fabricmc.loom.api.decompilers.DecompilationMetadata;
-import net.fabricmc.loom.api.decompilers.LoomDecompiler;
-import net.fabricmc.loom.util.ConsumingOutputStream;
-import net.fabricmc.loom.util.OperatingSystem;
-
-public abstract class AbstractFernFlowerDecompiler implements LoomDecompiler {
- private final Project project;
-
- protected AbstractFernFlowerDecompiler(Project project) {
- this.project = project;
- }
-
- public abstract Class<? extends AbstractForkedFFExecutor> fernFlowerExecutor();
-
- @Override
- public void decompile(Path compiledJar, Path sourcesDestination, Path linemapDestination, DecompilationMetadata metaData) {
- if (!OperatingSystem.is64Bit()) {
- throw new UnsupportedOperationException("FernFlower decompiler requires a 64bit JVM to run due to the memory requirements");
- }
-
- project.getLogging().captureStandardOutput(LogLevel.LIFECYCLE);
-
- Map<String, Object> options = new HashMap<>() {{
- put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1");
- put(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1");
- put(IFernflowerPreferences.REMOVE_SYNTHETIC, "1");
- put(IFernflowerPreferences.LOG_LEVEL, "trace");
- put(IFernflowerPreferences.THREADS, metaData.numberOfThreads());
- put(IFernflowerPreferences.INDENT_STRING, "\t");
- }};
-
- List<String> args = new ArrayList<>();
-
- options.forEach((k, v) -> args.add(format("-{0}={1}", k, v)));
- args.add(absolutePathOf(compiledJar));
- args.add("-o=" + absolutePathOf(sourcesDestination));
- args.add("-l=" + absolutePathOf(linemapDestination));
- args.add("-m=" + absolutePathOf(metaData.javaDocs()));
-
- // TODO, Decompiler breaks on jemalloc, J9 module-info.class?
- for (Path library : metaData.libraries()) {
- args.add("-e=" + absolutePathOf(library));
- }
-
- ServiceRegistry registry = ((ProjectInternal) project).getServices();
- ProgressLoggerFactory factory = registry.get(ProgressLoggerFactory.class);
- ProgressLogger progressGroup = factory.newOperation(getClass()).setDescription("Decompile");
- Supplier<ProgressLogger> loggerFactory = () -> {
- ProgressLogger pl = factory.newOperation(getClass(), progressGroup);
- pl.setDescription("decompile worker");
- pl.started();
- return pl;
- };
- Stack<ProgressLogger> freeLoggers = new Stack<>();
- Map<String, ProgressLogger> inUseLoggers = new HashMap<>();
-
- progressGroup.started();
- ExecResult result = ForkingJavaExec.javaexec(
- project,
- spec -> {
- spec.getMainClass().set(fernFlowerExecutor().getName());
- spec.jvmArgs("-Xms200m", "-Xmx3G");
- spec.setArgs(args);
- spec.setErrorOutput(new ConsumingOutputStream(line -> {
- if (line.startsWith("Inconsistent inner class entries")) {
- // Suppress this
- return;
- }
-
- System.err.println(line);
- }));
- spec.setStandardOutput(new ConsumingOutputStream(line -> {
- if (line.startsWith("Listening for transport") || !line.contains("::")) {
- System.out.println(line);
- return;
- }
-
- int sepIdx = line.indexOf("::");
- String id = line.substring(0, sepIdx).trim();
- String data = line.substring(sepIdx + 2).trim();
-
- ProgressLogger logger = inUseLoggers.get(id);
-
- String[] segs = data.split(" ");
-
- if (segs[0].equals("waiting")) {
- if (logger != null) {
- logger.progress("Idle..");
- inUseLoggers.remove(id);
- freeLoggers.push(logger);
- }
- } else {
- if (logger == null) {
- if (!freeLoggers.isEmpty()) {
- logger = freeLoggers.pop();
- } else {
- logger = loggerFactory.get();
- }
-
- inUseLoggers.put(id, logger);
- }
-
- logger.progress(data);
- }
- }));
- });
- inUseLoggers.values().forEach(ProgressLogger::completed);
- freeLoggers.forEach(ProgressLogger::completed);
- progressGroup.completed();
-
- result.rethrowFailure();
- result.assertNormalExitValue();
- }
-
- private static String absolutePathOf(Path path) {
- return path.toAbsolutePath().toString();
- }
-}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractForkedFFExecutor.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractForkedFFExecutor.java
deleted file mode 100644
index 2435233e..00000000
--- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractForkedFFExecutor.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * This file is part of fabric-loom, licensed under the MIT License (MIT).
- *
- * Copyright (c) 2019-2020 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.fernflower;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Entry point for Forked FernFlower task.
- * Takes one parameter, a single file, each line is treated as command line input.
- * Forces one input file.
- * Forces one output file using '-o=/path/to/output'
- * Created by covers1624 on 11/02/19.
- * <p>Extending classes MUST have a standard "public static void main(args)".
- * They may then call AbstractForkedFFExecutor#decompile for it to use the overridden AbstractForkedFFExecutor#runFF
- * </p>
- */
-public abstract class AbstractForkedFFExecutor {
- public static void decompile(String[] args, AbstractForkedFFExecutor ffExecutor) {
- Map<String, Object> options = new HashMap<>();
- File input = null;
- File output = null;
- File lineMap = null;
- File mappings = null;
- List<File> libraries = new ArrayList<>();
-
- boolean isOption = true;
-
- for (String arg : args) {
- if (isOption && arg.length() > 5 && arg.charAt(0) == '-' && arg.charAt(4) == '=') {
- String value = arg.substring(5);
-
- if ("true".equalsIgnoreCase(value)) {
- value = "1";
- } else if ("false".equalsIgnoreCase(value)) {
- value = "0";
- }
-
- options.put(arg.substring(1, 4), value);
- } else {
- isOption = false;
-
- if (arg.startsWith("-e=")) {
- libraries.add(new File(arg.substring(3)));
- } else if (arg.startsWith("-o=")) {
- if (output != null) {
- throw new RuntimeException("Unable to set more than one output.");
- }
-
- output = new File(arg.substring(3));
- } else if (arg.startsWith("-l=")) {
- if (lineMap != null) {
- throw new RuntimeException("Unable to set more than one lineMap file.");
- }
-
- lineMap = new File(arg.substring(3));
- } else if (arg.startsWith("-m=")) {
- if (mappings != null) {
- throw new RuntimeException("Unable to use more than one mappings file.");
- }
-
- mappings = new File(arg.substring(3));
- } else {
- if (input != null) {
- throw new RuntimeException("Unable to set more than one input.");
- }
-
- input = new File(arg);
- }
- }
- }
-
- Objects.requireNonNull(input, "Input not set.");
- Objects.requireNonNull(output, "Output not set.");
- Objects.requireNonNull(mappings, "Mappings not set.");
-
- ffExecutor.runFF(options, libraries, input, output, lineMap, mappings);
- }
-
- public abstract void runFF(Map<String, Object> options, List<File> libraries, File input, File output, File lineMap, File mappings);
-}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricFernFlowerDecompiler.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricFernFlowerDecompiler.java
index b3663773..c3009448 100644
--- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricFernFlowerDecompiler.java
+++ b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricFernFlowerDecompiler.java
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
- * Copyright (c) 2018-2020 FabricMC
+ * Copyright (c) 2019-2021 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
@@ -24,20 +24,43 @@
package net.fabricmc.loom.decompilers.fernflower;
-import org.gradle.api.Project;
+import java.nio.file.Path;
+import java.util.Map;
-public class FabricFernFlowerDecompiler extends AbstractFernFlowerDecompiler {
- public FabricFernFlowerDecompiler(Project project) {
- super(project);
- }
+import org.jetbrains.java.decompiler.main.Fernflower;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+import org.jetbrains.java.decompiler.main.extern.IResultSaver;
+
+import net.fabricmc.fernflower.api.IFabricJavadocProvider;
+import net.fabricmc.loom.api.decompilers.DecompilationMetadata;
+import net.fabricmc.loom.api.decompilers.LoomDecompiler;
+public final class FabricFernFlowerDecompiler implements LoomDecompiler {
@Override
public String name() {
return "FabricFlower"; // Or something else?
}
@Override
- public Class<? extends AbstractForkedFFExecutor> fernFlowerExecutor() {
- return FabricForkedFFExecutor.class;
+ public void decompile(Path compiledJar, Path sourcesDestination, Path linemapDestination, DecompilationMetadata metaData) {
+ Map<String, Object> options = Map.of(
+ IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1",
+ IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1",
+ IFernflowerPreferences.REMOVE_SYNTHETIC, "1",
+ IFernflowerPreferences.LOG_LEVEL, "trace",
+ IFernflowerPreferences.THREADS, String.valueOf(metaData.numberOfThreads()),
+ IFernflowerPreferences.INDENT_STRING, "\t",
+ IFabricJavadocProvider.PROPERTY_NAME, new TinyJavadocProvider(metaData.javaDocs().toFile())
+ );
+
+ IResultSaver saver = new ThreadSafeResultSaver(sourcesDestination::toFile, linemapDestination::toFile);
+ Fernflower ff = new Fernflower(FernFlowerUtils::getBytecode, saver, options, new FernflowerLogger(metaData.logger()));
+
+ for (Path library : metaData.libraries()) {
+ ff.addLibrary(library.toFile());
+ }
+
+ ff.addSource(compiledJar.toFile());
+ ff.decompileContext();
}
}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricForkedFFExecutor.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernflowerLogger.java
index e88c8086..a98060e9 100644
--- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FabricForkedFFExecutor.java
+++ b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernflowerLogger.java
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
- * Copyright (c) 2018-2020 FabricMC
+ * Copyright (c) 2021 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
@@ -24,34 +24,60 @@
package net.fabricmc.loom.decompilers.fernflower;
-import java.io.File;
-import java.util.List;
-import java.util.Map;
+import java.io.IOException;
-import org.jetbrains.java.decompiler.main.Fernflower;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
-import org.jetbrains.java.decompiler.main.extern.IResultSaver;
-import net.fabricmc.fernflower.api.IFabricJavadocProvider;
+import net.fabricmc.loom.util.IOStringConsumer;
-public class FabricForkedFFExecutor extends AbstractForkedFFExecutor {
- public static void main(String[] args) {
- AbstractForkedFFExecutor.decompile(args, new FabricForkedFFExecutor());
+public class FernflowerLogger extends IFernflowerLogger {
+ private final IOStringConsumer logger;
+
+ public FernflowerLogger(IOStringConsumer logger) {
+ this.logger = logger;
}
@Override
- public void runFF(Map<String, Object> options, List<File> libraries, File input, File output, File lineMap, File mappings) {
- options.put(IFabricJavadocProvider.PROPERTY_NAME, new TinyJavadocProvider(mappings));
+ public void writeMessage(String message, Severity severity) {
+ if (message.contains("Inconsistent inner class entries for")) return;
+ System.err.println(message);
+ }
- IResultSaver saver = new ThreadSafeResultSaver(() -> output, () -> lineMap);
- IFernflowerLogger logger = new ThreadIDFFLogger();
- Fernflower ff = new Fernflower(FernFlowerUtils::getBytecode, saver, options, logger);
+ @Override
+ public void writeMessage(String message, Severity severity, Throwable t) {
+ writeMessage(message, severity);
+ }
- for (File library : libraries) {
- ff.addLibrary(library);
+ private void write(String data) {
+ try {
+ logger.accept(data);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to log", e);
}
+ }
+
+ @Override
+ public void startReadingClass(String className) {
+ write("Decompiling " + className);
+ }
+
+ @Override
+ public void startClass(String className) {
+ write("Decompiling " + className);
+ }
+
+ @Override
+ public void startWriteClass(String className) {
+ // Nope
+ }
- ff.addSource(input);
- ff.decompileContext();
+ @Override
+ public void startMethod(String methodName) {
+ // Nope
+ }
+
+ @Override
+ public void endMethod() {
+ // Nope
}
}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java
deleted file mode 100644
index bbcd80de..00000000
--- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * This file is part of fabric-loom, licensed under the MIT License (MIT).
- *
- * Copyright (c) 2016-2021 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.fernflower;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-
-import org.gradle.api.Action;
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.api.file.FileCollection;
-import org.gradle.process.ExecResult;
-import org.gradle.process.JavaExecSpec;
-
-/**
- * Simple utility class for a Task that wishes to execute a java process
- * with the classpath of the gradle plugin plus groovy.
- *
- * <p>Created by covers1624 on 11/02/19.
- */
-public class ForkingJavaExec {
- public static ExecResult javaexec(Project project, Action<? super JavaExecSpec> action) {
- return project.javaexec(spec -> {
- spec.classpath(getClasspath(project));
- action.execute(spec);
- });
- }
-
- private static Object getClasspath(Project project) {
- if (System.getProperty("fabric.loom.test") != null) {
- return getTestClasspath();
- }
-
- return getRuntimeClasspath(project.getRootProject().getPlugins().hasPlugin("fabric-loom") ? project.getRootProject() : project);
- }
-
- private static FileCollection getRuntimeClasspath(Project project) {
- ConfigurationContainer configurations = project.getBuildscript().getConfigurations();
- DependencyHandler handler = project.getDependencies();
- return configurations.getByName("classpath")
- .plus(configurations.detachedConfiguration(handler.localGroovy()));
- }
-
- private static URL[] getTestClasspath() {
- return ((URLClassLoader) ForkingJavaExec.class.getClassLoader()).getURLs();
- }
-}
diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/ThreadIDFFLogger.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/ThreadIDFFLogger.java
deleted file mode 100644
index a7b617bc..00000000
--- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/ThreadIDFFLogger.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * This file is part of fabric-loom, licensed under the MIT License (MIT).
- *
- * Copyright (c) 2019-2020 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.fernflower;
-
-import java.io.PrintStream;
-import java.text.MessageFormat;
-import java.util.Stack;
-
-import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
-
-/**
- * This logger simply prints what each thread is doing
- * to the console in a machine parsable way.
- *
- * <p>Created by covers1624 on 11/02/19.
- */
-public class ThreadIDFFLogger extends IFernflowerLogger {
- public final PrintStream stdOut;
- public final PrintStream stdErr;
-
- private final ThreadLocal<Stack<String>> workingClass = ThreadLocal.withInitial(Stack::new);
- private final ThreadLocal<Stack<String>> line = ThreadLocal.withInitial(Stack::new);
-
- public ThreadIDFFLogger() {
- this(System.err, System.out);
- }
-
- public ThreadIDFFLogger(PrintStream stdOut, PrintStream stdErr) {
- this.stdOut = stdOut;
- this.stdErr = stdErr;
- }
-
- @Override
- public void writeMessage(String message, Severity severity) {
- System.err.println(message);
- }
-
- @Override
- public void writeMessage(String message, Severity severity, Throwable t) {
- System.err.println(message);
- t.printStackTrace(System.err);
- }
-
- private void print() {
- Thread thread = Thread.currentThread();
- long id = thread.getId();
-
- if (line.get().isEmpty()) {
- System.out.println(MessageFormat.format("{0} :: waiting", id));
- return;
- }
-
- String line = this.line.get().peek();
- System.out.println(MessageFormat.format("{0} :: {1}", id, line).trim());
- }
-
- @Override
- public void startReadingClass(String className) {
- workingClass.get().push(className);
- line.get().push("Decompiling " + className);
- print();
- }
-
- @Override
- public void startClass(String className) {
- workingClass.get().push(className);
- line.get().push("Decompiling " + className);
- print();
- }
-
- @Override
- public void startMethod(String methodName) {
- // No need to print out methods
- }
-
- @Override
- public void endMethod() {
- }
-
- @Override
- public void endClass() {
- line.get().pop();
- workingClass.get().pop();
- print();
- }
-
- @Override
- public void startWriteClass(String className) {
- line.get().push("Writing " + className);
- print();
- }
-
- @Override
- public void endWriteClass() {
- line.get().pop();
- print();
- }
-
- @Override
- public void endReadingClass() {
- line.get().pop();
- workingClass.get().pop();
- print();
- }
-}