summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
blob: 9e37f168c04b5f5fc988a061ade9343ff1d3067c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package cuchaz.enigma.source.cfr;

import com.google.common.io.ByteStreams;
import cuchaz.enigma.ClassProvider;
import cuchaz.enigma.source.Decompiler;
import cuchaz.enigma.source.Source;
import cuchaz.enigma.source.SourceSettings;
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.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;


public class CfrDecompiler implements Decompiler {
    private final DCCommonState state;

    public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) {
        Map<String, String> options = new HashMap<>();

        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<String> addJar(String jarPath) {
                return null;
            }

            @Override
            public String getPossiblyRenamedPath(String path) {
                return path;
            }

            @Override
            public Pair<byte[], String> getClassFileContent(String path) {
                ClassNode node = classProvider.getClassNode(path.substring(0, path.lastIndexOf('.')));

                if (node == null) {
                    try (InputStream classResource = CfrDecompiler.class.getClassLoader().getResourceAsStream(path)) {
                        if (classResource != null) {
                            return new Pair<>(ByteStreams.toByteArray(classResource), path);
                        }
                    } catch (IOException ignored) {}

                    return null;
                }

                ClassWriter cw = new ClassWriter(0);
                node.accept(cw);
                return new Pair<>(cw.toByteArray(), path);
            }
        });
    }

    @Override
    public Source getSource(String className) {
        DCCommonState state = this.state;
        Options options = state.getOptions();

        ObfuscationMapping mapping = MappingFactory.get(options, state);
        state = new DCCommonState(state, 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);
        return new CfrSource(tree, state, typeUsageCollector.getRealTypeUsageInformation());
    }
}