diff options
| author | 2022-04-15 13:41:30 +0100 | |
|---|---|---|
| committer | 2022-04-15 13:41:30 +0100 | |
| commit | 9d7f57dadcd67342f8fa7b8618424f588ffcd28c (patch) | |
| tree | e4f5bcef352c6772aceac5ed0cd1d72c91636b5b | |
| parent | Fix remapping of field Handle's (diff) | |
| download | enigma-9d7f57dadcd67342f8fa7b8618424f588ffcd28c.tar.gz enigma-9d7f57dadcd67342f8fa7b8618424f588ffcd28c.tar.xz enigma-9d7f57dadcd67342f8fa7b8618424f588ffcd28c.zip | |
Fix memory leak with CFR (#448)
| -rw-r--r-- | enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java | 91 | ||||
| -rw-r--r-- | enigma/src/main/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; | |||
| 9 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; | 9 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; |
| 10 | import org.benf.cfr.reader.apiunreleased.JarContent; | 10 | import org.benf.cfr.reader.apiunreleased.JarContent; |
| 11 | import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; | 11 | import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; |
| 12 | import org.benf.cfr.reader.entities.ClassFile; | ||
| 13 | import org.benf.cfr.reader.mapping.MappingFactory; | ||
| 14 | import org.benf.cfr.reader.mapping.ObfuscationMapping; | ||
| 15 | import org.benf.cfr.reader.relationship.MemberNameResolver; | ||
| 16 | import org.benf.cfr.reader.state.DCCommonState; | ||
| 17 | import org.benf.cfr.reader.state.TypeUsageCollectingDumper; | ||
| 18 | import org.benf.cfr.reader.util.AnalysisType; | 12 | import org.benf.cfr.reader.util.AnalysisType; |
| 19 | import org.benf.cfr.reader.util.CannotLoadClassException; | ||
| 20 | import org.benf.cfr.reader.util.collections.ListFactory; | ||
| 21 | import org.benf.cfr.reader.util.getopt.Options; | 13 | import org.benf.cfr.reader.util.getopt.Options; |
| 22 | import org.benf.cfr.reader.util.getopt.OptionsImpl; | 14 | import org.benf.cfr.reader.util.getopt.OptionsImpl; |
| 23 | import org.checkerframework.checker.nullness.qual.Nullable; | 15 | import org.checkerframework.checker.nullness.qual.Nullable; |
| @@ -27,76 +19,51 @@ import java.util.Collection; | |||
| 27 | import java.util.Map; | 19 | import java.util.Map; |
| 28 | 20 | ||
| 29 | public class CfrDecompiler implements Decompiler { | 21 | public class CfrDecompiler implements Decompiler { |
| 30 | private final DCCommonState state; | ||
| 31 | // cfr doesn't add final on params so final setting is ignored | 22 | // cfr doesn't add final on params so final setting is ignored |
| 32 | private final SourceSettings settings; | 23 | private final SourceSettings settings; |
| 24 | private final Options options; | ||
| 25 | private final ClassFileSource2 classFileSource; | ||
| 33 | 26 | ||
| 34 | public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { | 27 | public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { |
| 35 | Map<String, String> options = Map.of("trackbytecodeloc", "true"); | 28 | this.options = OptionsImpl.getFactory().create( Map.of("trackbytecodeloc", "true")); |
| 36 | |||
| 37 | state = new DCCommonState(OptionsImpl.getFactory().create(options), new ClassFileSource2() { | ||
| 38 | @Override | ||
| 39 | public JarContent addJarContent(String s, AnalysisType analysisType) { | ||
| 40 | return null; | ||
| 41 | } | ||
| 42 | |||
| 43 | @Override | ||
| 44 | public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { | ||
| 45 | |||
| 46 | } | ||
| 47 | |||
| 48 | @Override | ||
| 49 | public Collection<String> addJar(String jarPath) { | ||
| 50 | return null; | ||
| 51 | } | ||
| 52 | |||
| 53 | @Override | ||
| 54 | public String getPossiblyRenamedPath(String path) { | ||
| 55 | return path; | ||
| 56 | } | ||
| 57 | |||
| 58 | @Override | ||
| 59 | public Pair<byte[], String> getClassFileContent(String path) { | ||
| 60 | ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); | ||
| 61 | |||
| 62 | if (node == null) { | ||
| 63 | return null; | ||
| 64 | } | ||
| 65 | |||
| 66 | return new Pair<>(AsmUtil.nodeToBytes(node), path); | ||
| 67 | } | ||
| 68 | }); | ||
| 69 | |||
| 70 | this.settings = sourceSettings; | 29 | this.settings = sourceSettings; |
| 30 | this.classFileSource = new ClassFileSource(classProvider); | ||
| 71 | } | 31 | } |
| 72 | 32 | ||
| 73 | @Override | 33 | @Override |
| 74 | public Source getSource(String className, @Nullable EntryRemapper mapper) { | 34 | public Source getSource(String className, @Nullable EntryRemapper mapper) { |
| 75 | DCCommonState state = this.state; | 35 | return new CfrSource(className, settings, this.options, this.classFileSource, mapper); |
| 76 | Options options = state.getOptions(); | 36 | } |
| 77 | |||
| 78 | ObfuscationMapping mapping = MappingFactory.get(options, state); | ||
| 79 | state = new DCCommonState(state, mapping); | ||
| 80 | ClassFile tree = state.getClassFileMaybePath(className); | ||
| 81 | 37 | ||
| 82 | state.configureWith(tree); | 38 | private record ClassFileSource(ClassProvider classProvider) implements ClassFileSource2 { |
| 39 | @Override | ||
| 40 | public JarContent addJarContent(String s, AnalysisType analysisType) { | ||
| 41 | return null; | ||
| 42 | } | ||
| 83 | 43 | ||
| 84 | // To make sure we're analysing the cached version | 44 | @Override |
| 85 | try { | 45 | public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { |
| 86 | tree = state.getClassFile(tree.getClassType()); | ||
| 87 | } catch (CannotLoadClassException ignored) { | ||
| 88 | } | 46 | } |
| 89 | 47 | ||
| 90 | if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { | 48 | @Override |
| 91 | tree.loadInnerClasses(state); | 49 | public Collection<String> addJar(String jarPath) { |
| 50 | return null; | ||
| 92 | } | 51 | } |
| 93 | 52 | ||
| 94 | if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { | 53 | @Override |
| 95 | MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); | 54 | public String getPossiblyRenamedPath(String path) { |
| 55 | return path; | ||
| 96 | } | 56 | } |
| 97 | 57 | ||
| 98 | TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); | 58 | @Override |
| 99 | tree.analyseTop(state, typeUsageCollector); | 59 | public Pair<byte[], String> getClassFileContent(String path) { |
| 100 | return new CfrSource(settings, tree, state, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); | 60 | ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); |
| 61 | |||
| 62 | if (node == null) { | ||
| 63 | return null; | ||
| 64 | } | ||
| 65 | |||
| 66 | return new Pair<>(AsmUtil.nodeToBytes(node), path); | ||
| 67 | } | ||
| 101 | } | 68 | } |
| 102 | } | 69 | } |
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; | |||
| 4 | import cuchaz.enigma.source.SourceIndex; | 4 | import cuchaz.enigma.source.SourceIndex; |
| 5 | import cuchaz.enigma.source.SourceSettings; | 5 | import cuchaz.enigma.source.SourceSettings; |
| 6 | import cuchaz.enigma.translation.mapping.EntryRemapper; | 6 | import cuchaz.enigma.translation.mapping.EntryRemapper; |
| 7 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; | ||
| 7 | import org.benf.cfr.reader.entities.ClassFile; | 8 | import org.benf.cfr.reader.entities.ClassFile; |
| 9 | import org.benf.cfr.reader.mapping.MappingFactory; | ||
| 10 | import org.benf.cfr.reader.mapping.ObfuscationMapping; | ||
| 11 | import org.benf.cfr.reader.relationship.MemberNameResolver; | ||
| 8 | import org.benf.cfr.reader.state.DCCommonState; | 12 | import org.benf.cfr.reader.state.DCCommonState; |
| 9 | import org.benf.cfr.reader.state.TypeUsageInformation; | 13 | import org.benf.cfr.reader.state.TypeUsageCollectingDumper; |
| 14 | import org.benf.cfr.reader.util.CannotLoadClassException; | ||
| 15 | import org.benf.cfr.reader.util.collections.ListFactory; | ||
| 10 | import org.benf.cfr.reader.util.getopt.Options; | 16 | import org.benf.cfr.reader.util.getopt.Options; |
| 17 | import org.benf.cfr.reader.util.getopt.OptionsImpl; | ||
| 11 | import org.checkerframework.checker.nullness.qual.Nullable; | 18 | import org.checkerframework.checker.nullness.qual.Nullable; |
| 12 | 19 | ||
| 13 | public class CfrSource implements Source { | 20 | public class CfrSource implements Source { |
| 21 | private final String className; | ||
| 14 | private final SourceSettings settings; | 22 | private final SourceSettings settings; |
| 15 | private final ClassFile tree; | ||
| 16 | private final DCCommonState state; | ||
| 17 | private final TypeUsageInformation typeUsage; | ||
| 18 | private final Options options; | 23 | private final Options options; |
| 24 | private final ClassFileSource2 classFileSource; | ||
| 19 | private final EntryRemapper mapper; | 25 | private final EntryRemapper mapper; |
| 20 | 26 | ||
| 21 | private SourceIndex index; | 27 | private SourceIndex index; |
| 22 | 28 | ||
| 23 | public CfrSource(SourceSettings settings, ClassFile tree, DCCommonState state, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) { | 29 | public CfrSource(String className, SourceSettings settings, Options options, ClassFileSource2 classFileSource, @Nullable EntryRemapper mapper) { |
| 30 | this.className = className; | ||
| 24 | this.settings = settings; | 31 | this.settings = settings; |
| 25 | this.tree = tree; | ||
| 26 | this.state = state; | ||
| 27 | this.typeUsage = typeUsage; | ||
| 28 | this.options = options; | 32 | this.options = options; |
| 33 | this.classFileSource = classFileSource; | ||
| 29 | this.mapper = mapper; | 34 | this.mapper = mapper; |
| 30 | } | 35 | } |
| 31 | 36 | ||
| 32 | @Override | 37 | @Override |
| 33 | public Source withJavadocs(EntryRemapper mapper) { | 38 | public Source withJavadocs(EntryRemapper mapper) { |
| 34 | return new CfrSource(settings, tree, state, typeUsage, options, mapper); | 39 | return new CfrSource(className, settings, options, classFileSource, mapper); |
| 35 | } | 40 | } |
| 36 | 41 | ||
| 37 | @Override | 42 | @Override |
| @@ -46,12 +51,36 @@ public class CfrSource implements Source { | |||
| 46 | return index.getSource(); | 51 | return index.getSource(); |
| 47 | } | 52 | } |
| 48 | 53 | ||
| 49 | private synchronized void ensureDecompiled() { | 54 | private void ensureDecompiled() { |
| 50 | if (index != null) { | 55 | if (index != null) { |
| 51 | return; | 56 | return; |
| 52 | } | 57 | } |
| 53 | 58 | ||
| 54 | EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsage, options, mapper); | 59 | DCCommonState commonState = new DCCommonState(options, classFileSource); |
| 60 | ObfuscationMapping mapping = MappingFactory.get(options, commonState); | ||
| 61 | DCCommonState state = new DCCommonState(commonState, mapping); | ||
| 62 | ClassFile tree = state.getClassFileMaybePath(className); | ||
| 63 | |||
| 64 | state.configureWith(tree); | ||
| 65 | |||
| 66 | // To make sure we're analysing the cached version | ||
| 67 | try { | ||
| 68 | tree = state.getClassFile(tree.getClassType()); | ||
| 69 | } catch (CannotLoadClassException ignored) { | ||
| 70 | } | ||
| 71 | |||
| 72 | if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { | ||
| 73 | tree.loadInnerClasses(state); | ||
| 74 | } | ||
| 75 | |||
| 76 | if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { | ||
| 77 | MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); | ||
| 78 | } | ||
| 79 | |||
| 80 | TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); | ||
| 81 | tree.analyseTop(state, typeUsageCollector); | ||
| 82 | |||
| 83 | EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); | ||
| 55 | tree.dump(state.getObfuscationMapping().wrap(dumper)); | 84 | tree.dump(state.getObfuscationMapping().wrap(dumper)); |
| 56 | index = dumper.getIndex(); | 85 | index = dumper.getIndex(); |
| 57 | } | 86 | } |