diff options
author | modmuss50 <modmuss50@gmail.com> | 2021-10-11 13:47:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-11 13:47:16 +0100 |
commit | e2439b7f57a82c365d4726d068b68ea2eb606f78 (patch) | |
tree | 5cc78058c70e46e22d1ada1a4411c9356cbdf906 /src/main/java/net/fabricmc/loom/decompilers | |
parent | 5315d3c5b24bcce47268ef8962c07aeb934b014b (diff) | |
download | architectury-loom-e2439b7f57a82c365d4726d068b68ea2eb606f78.tar.gz architectury-loom-e2439b7f57a82c365d4726d068b68ea2eb606f78.tar.bz2 architectury-loom-e2439b7f57a82c365d4726d068b68ea2eb606f78.zip |
Rewrite GenSources including full support for CFR. (#511)
* Rewrite CFR decompiler interface. Support javadoc
* CFR line numbers and fixes.
* Cleanup and fix
* Use WorkerExecutor to fork, massively cleans up the fernflower code, but does remove the fancy multithreaded logging.
* Use IPC to get logging back from the decompilers.
* Cleanup UnpickJarTask, fix leak in IPCServer
* Used published CFR build
* Handle older windows versions that do not support AF_UNIX.
* Fixes and basic unit test
* Improve memory handling of genSources
* Stop decompile worker JVM
Diffstat (limited to 'src/main/java/net/fabricmc/loom/decompilers')
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(); - } -} |