From 9d7f57dadcd67342f8fa7b8618424f588ffcd28c Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Fri, 15 Apr 2022 13:41:30 +0100 Subject: Fix memory leak with CFR (#448) --- .../cuchaz/enigma/source/cfr/CfrDecompiler.java | 91 +++++++--------------- .../java/cuchaz/enigma/source/cfr/CfrSource.java | 51 +++++++++--- 2 files changed, 69 insertions(+), 73 deletions(-) diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java index 91fd5a27..cd7b2aa2 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java @@ -9,15 +9,7 @@ import cuchaz.enigma.utils.AsmUtil; import org.benf.cfr.reader.apiunreleased.ClassFileSource2; import org.benf.cfr.reader.apiunreleased.JarContent; import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; -import org.benf.cfr.reader.entities.ClassFile; -import org.benf.cfr.reader.mapping.MappingFactory; -import org.benf.cfr.reader.mapping.ObfuscationMapping; -import org.benf.cfr.reader.relationship.MemberNameResolver; -import org.benf.cfr.reader.state.DCCommonState; -import org.benf.cfr.reader.state.TypeUsageCollectingDumper; import org.benf.cfr.reader.util.AnalysisType; -import org.benf.cfr.reader.util.CannotLoadClassException; -import org.benf.cfr.reader.util.collections.ListFactory; import org.benf.cfr.reader.util.getopt.Options; import org.benf.cfr.reader.util.getopt.OptionsImpl; import org.checkerframework.checker.nullness.qual.Nullable; @@ -27,76 +19,51 @@ import java.util.Collection; import java.util.Map; public class CfrDecompiler implements Decompiler { - private final DCCommonState state; // cfr doesn't add final on params so final setting is ignored private final SourceSettings settings; + private final Options options; + private final ClassFileSource2 classFileSource; public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { - Map options = Map.of("trackbytecodeloc", "true"); - - state = new DCCommonState(OptionsImpl.getFactory().create(options), new ClassFileSource2() { - @Override - public JarContent addJarContent(String s, AnalysisType analysisType) { - return null; - } - - @Override - public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { - - } - - @Override - public Collection addJar(String jarPath) { - return null; - } - - @Override - public String getPossiblyRenamedPath(String path) { - return path; - } - - @Override - public Pair getClassFileContent(String path) { - ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); - - if (node == null) { - return null; - } - - return new Pair<>(AsmUtil.nodeToBytes(node), path); - } - }); - + this.options = OptionsImpl.getFactory().create( Map.of("trackbytecodeloc", "true")); this.settings = sourceSettings; + this.classFileSource = new ClassFileSource(classProvider); } @Override public Source getSource(String className, @Nullable EntryRemapper mapper) { - DCCommonState state = this.state; - Options options = state.getOptions(); - - ObfuscationMapping mapping = MappingFactory.get(options, state); - state = new DCCommonState(state, mapping); - ClassFile tree = state.getClassFileMaybePath(className); + return new CfrSource(className, settings, this.options, this.classFileSource, mapper); + } - state.configureWith(tree); + private record ClassFileSource(ClassProvider classProvider) implements ClassFileSource2 { + @Override + public JarContent addJarContent(String s, AnalysisType analysisType) { + return null; + } - // To make sure we're analysing the cached version - try { - tree = state.getClassFile(tree.getClassType()); - } catch (CannotLoadClassException ignored) { + @Override + public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { } - if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { - tree.loadInnerClasses(state); + @Override + public Collection addJar(String jarPath) { + return null; } - if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { - MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); + @Override + public String getPossiblyRenamedPath(String path) { + return path; } - TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); - tree.analyseTop(state, typeUsageCollector); - return new CfrSource(settings, tree, state, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); + @Override + public Pair getClassFileContent(String path) { + ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); + + if (node == null) { + return null; + } + + return new Pair<>(AsmUtil.nodeToBytes(node), path); + } } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java index 7c63afcc..fb44ef9a 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java @@ -4,34 +4,39 @@ import cuchaz.enigma.source.Source; import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.source.SourceSettings; import cuchaz.enigma.translation.mapping.EntryRemapper; +import org.benf.cfr.reader.apiunreleased.ClassFileSource2; import org.benf.cfr.reader.entities.ClassFile; +import org.benf.cfr.reader.mapping.MappingFactory; +import org.benf.cfr.reader.mapping.ObfuscationMapping; +import org.benf.cfr.reader.relationship.MemberNameResolver; import org.benf.cfr.reader.state.DCCommonState; -import org.benf.cfr.reader.state.TypeUsageInformation; +import org.benf.cfr.reader.state.TypeUsageCollectingDumper; +import org.benf.cfr.reader.util.CannotLoadClassException; +import org.benf.cfr.reader.util.collections.ListFactory; import org.benf.cfr.reader.util.getopt.Options; +import org.benf.cfr.reader.util.getopt.OptionsImpl; import org.checkerframework.checker.nullness.qual.Nullable; public class CfrSource implements Source { + private final String className; private final SourceSettings settings; - private final ClassFile tree; - private final DCCommonState state; - private final TypeUsageInformation typeUsage; private final Options options; + private final ClassFileSource2 classFileSource; private final EntryRemapper mapper; private SourceIndex index; - public CfrSource(SourceSettings settings, ClassFile tree, DCCommonState state, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) { + public CfrSource(String className, SourceSettings settings, Options options, ClassFileSource2 classFileSource, @Nullable EntryRemapper mapper) { + this.className = className; this.settings = settings; - this.tree = tree; - this.state = state; - this.typeUsage = typeUsage; this.options = options; + this.classFileSource = classFileSource; this.mapper = mapper; } @Override public Source withJavadocs(EntryRemapper mapper) { - return new CfrSource(settings, tree, state, typeUsage, options, mapper); + return new CfrSource(className, settings, options, classFileSource, mapper); } @Override @@ -46,12 +51,36 @@ public class CfrSource implements Source { return index.getSource(); } - private synchronized void ensureDecompiled() { + private void ensureDecompiled() { if (index != null) { return; } - EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsage, options, mapper); + DCCommonState commonState = new DCCommonState(options, classFileSource); + ObfuscationMapping mapping = MappingFactory.get(options, commonState); + DCCommonState state = new DCCommonState(commonState, mapping); + ClassFile tree = state.getClassFileMaybePath(className); + + state.configureWith(tree); + + // To make sure we're analysing the cached version + try { + tree = state.getClassFile(tree.getClassType()); + } catch (CannotLoadClassException ignored) { + } + + if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { + tree.loadInnerClasses(state); + } + + if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { + MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); + } + + TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); + tree.analyseTop(state, typeUsageCollector); + + EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); tree.dump(state.getObfuscationMapping().wrap(dumper)); index = dumper.getIndex(); } -- cgit v1.2.3