summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/source
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/source')
-rw-r--r--src/main/java/cuchaz/enigma/source/Decompiler.java5
-rw-r--r--src/main/java/cuchaz/enigma/source/DecompilerService.java11
-rw-r--r--src/main/java/cuchaz/enigma/source/Decompilers.java9
-rw-r--r--src/main/java/cuchaz/enigma/source/Source.java11
-rw-r--r--src/main/java/cuchaz/enigma/source/SourceIndex.java174
-rw-r--r--src/main/java/cuchaz/enigma/source/SourceSettings.java11
-rw-r--r--src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java108
-rw-r--r--src/main/java/cuchaz/enigma/source/cfr/CfrSource.java38
-rw-r--r--src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java433
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/EntryParser.java49
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java81
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java49
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java95
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java218
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java40
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java46
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java134
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/DropImportAstTransform.java33
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java37
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java29
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java107
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java414
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java39
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java197
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java33
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java38
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java140
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java38
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java20
29 files changed, 0 insertions, 2637 deletions
diff --git a/src/main/java/cuchaz/enigma/source/Decompiler.java b/src/main/java/cuchaz/enigma/source/Decompiler.java
deleted file mode 100644
index c9666d5..0000000
--- a/src/main/java/cuchaz/enigma/source/Decompiler.java
+++ /dev/null
@@ -1,5 +0,0 @@
1package cuchaz.enigma.source;
2
3public interface Decompiler {
4 Source getSource(String className);
5}
diff --git a/src/main/java/cuchaz/enigma/source/DecompilerService.java b/src/main/java/cuchaz/enigma/source/DecompilerService.java
deleted file mode 100644
index 377ccbc..0000000
--- a/src/main/java/cuchaz/enigma/source/DecompilerService.java
+++ /dev/null
@@ -1,11 +0,0 @@
1package cuchaz.enigma.source;
2
3import cuchaz.enigma.ClassProvider;
4import cuchaz.enigma.api.service.EnigmaService;
5import cuchaz.enigma.api.service.EnigmaServiceType;
6
7public interface DecompilerService extends EnigmaService {
8 EnigmaServiceType<DecompilerService> TYPE = EnigmaServiceType.create("decompiler");
9
10 Decompiler create(ClassProvider classProvider, SourceSettings settings);
11}
diff --git a/src/main/java/cuchaz/enigma/source/Decompilers.java b/src/main/java/cuchaz/enigma/source/Decompilers.java
deleted file mode 100644
index 7d154a6..0000000
--- a/src/main/java/cuchaz/enigma/source/Decompilers.java
+++ /dev/null
@@ -1,9 +0,0 @@
1package cuchaz.enigma.source;
2
3import cuchaz.enigma.source.cfr.CfrDecompiler;
4import cuchaz.enigma.source.procyon.ProcyonDecompiler;
5
6public class Decompilers {
7 public static final DecompilerService PROCYON = ProcyonDecompiler::new;
8 public static final DecompilerService CFR = CfrDecompiler::new;
9}
diff --git a/src/main/java/cuchaz/enigma/source/Source.java b/src/main/java/cuchaz/enigma/source/Source.java
deleted file mode 100644
index 43c4de0..0000000
--- a/src/main/java/cuchaz/enigma/source/Source.java
+++ /dev/null
@@ -1,11 +0,0 @@
1package cuchaz.enigma.source;
2
3import cuchaz.enigma.translation.mapping.EntryRemapper;
4
5public interface Source {
6 String asString();
7
8 Source addJavadocs(EntryRemapper remapper);
9
10 SourceIndex index();
11}
diff --git a/src/main/java/cuchaz/enigma/source/SourceIndex.java b/src/main/java/cuchaz/enigma/source/SourceIndex.java
deleted file mode 100644
index 6a335ec..0000000
--- a/src/main/java/cuchaz/enigma/source/SourceIndex.java
+++ /dev/null
@@ -1,174 +0,0 @@
1package cuchaz.enigma.source;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Lists;
5import com.google.common.collect.Maps;
6import com.google.common.collect.Multimap;
7import cuchaz.enigma.analysis.EntryReference;
8import cuchaz.enigma.analysis.Token;
9import cuchaz.enigma.gui.SourceRemapper;
10import cuchaz.enigma.translation.mapping.EntryResolver;
11import cuchaz.enigma.translation.mapping.ResolutionStrategy;
12import cuchaz.enigma.translation.representation.entry.Entry;
13
14import java.util.Collection;
15import java.util.List;
16import java.util.Map;
17import java.util.TreeMap;
18import java.util.stream.Collectors;
19
20public class SourceIndex {
21 private String source;
22 private List<Integer> lineOffsets;
23 private final TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference;
24 private final Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens;
25 private final Map<Entry<?>, Token> declarationToToken;
26
27 public SourceIndex() {
28 tokenToReference = new TreeMap<>();
29 referenceToTokens = HashMultimap.create();
30 declarationToToken = Maps.newHashMap();
31 }
32
33 public SourceIndex(String source) {
34 this();
35 setSource(source);
36 }
37
38 public void setSource(String source) {
39 this.source = source;
40 lineOffsets = Lists.newArrayList();
41 lineOffsets.add(0);
42
43 for (int i = 0; i < this.source.length(); i++) {
44 if (this.source.charAt(i) == '\n') {
45 lineOffsets.add(i + 1);
46 }
47 }
48 }
49
50 public String getSource() {
51 return source;
52 }
53
54 public int getLineNumber(int position) {
55 int line = 0;
56
57 for (int offset : lineOffsets) {
58 if (offset > position) {
59 break;
60 }
61
62 line++;
63 }
64
65 return line;
66 }
67
68 public int getColumnNumber(int position) {
69 return position - lineOffsets.get(getLineNumber(position) - 1) + 1;
70 }
71
72 public int getPosition(int line, int column) {
73 return lineOffsets.get(line - 1) + column - 1;
74 }
75
76 public Iterable<Entry<?>> declarations() {
77 return declarationToToken.keySet();
78 }
79
80 public Iterable<Token> declarationTokens() {
81 return declarationToToken.values();
82 }
83
84 public Token getDeclarationToken(Entry<?> entry) {
85 return declarationToToken.get(entry);
86 }
87
88 public void addDeclaration(Token token, Entry<?> deobfEntry) {
89 if (token != null) {
90 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text);
91 tokenToReference.put(token, reference);
92 referenceToTokens.put(reference, token);
93 declarationToToken.put(deobfEntry, token);
94 }
95 }
96
97 public Iterable<EntryReference<Entry<?>, Entry<?>>> references() {
98 return referenceToTokens.keySet();
99 }
100
101 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) {
102 if (token == null) {
103 return null;
104 }
105
106 return tokenToReference.get(token);
107 }
108
109 public Iterable<Token> referenceTokens() {
110 return tokenToReference.keySet();
111 }
112
113 public Token getReferenceToken(int pos) {
114 Token token = tokenToReference.floorKey(new Token(pos, pos, null));
115
116 if (token != null && token.contains(pos)) {
117 return token;
118 }
119
120 return null;
121 }
122
123 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) {
124 return referenceToTokens.get(deobfReference);
125 }
126
127 public void addReference(Token token, Entry<?> deobfEntry, Entry<?> deobfContext) {
128 if (token != null) {
129 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
130 tokenToReference.put(token, deobfReference);
131 referenceToTokens.put(deobfReference, token);
132 }
133 }
134
135 public void resolveReferences(EntryResolver resolver) {
136 // resolve all the classes in the source references
137 for (Token token : Lists.newArrayList(referenceToTokens.values())) {
138 EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token);
139 EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
140
141 // replace the reference
142 tokenToReference.replace(token, resolvedReference);
143
144 Collection<Token> tokens = referenceToTokens.removeAll(reference);
145 referenceToTokens.putAll(resolvedReference, tokens);
146 }
147 }
148
149 public SourceIndex remapTo(SourceRemapper.Result result) {
150 SourceIndex remapped = new SourceIndex(result.getSource());
151
152 for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) {
153 remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue()));
154 }
155
156 for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) {
157 EntryReference<Entry<?>, Entry<?>> reference = entry.getKey();
158 Collection<Token> oldTokens = entry.getValue();
159
160 Collection<Token> newTokens = oldTokens
161 .stream()
162 .map(result::getRemappedToken)
163 .collect(Collectors.toList());
164
165 remapped.referenceToTokens.putAll(reference, newTokens);
166 }
167
168 for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) {
169 remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue());
170 }
171
172 return remapped;
173 }
174}
diff --git a/src/main/java/cuchaz/enigma/source/SourceSettings.java b/src/main/java/cuchaz/enigma/source/SourceSettings.java
deleted file mode 100644
index f6c68e9..0000000
--- a/src/main/java/cuchaz/enigma/source/SourceSettings.java
+++ /dev/null
@@ -1,11 +0,0 @@
1package cuchaz.enigma.source;
2
3public class SourceSettings {
4 public final boolean removeImports;
5 public final boolean removeVariableFinal;
6
7 public SourceSettings(boolean removeImports, boolean removeVariableFinal) {
8 this.removeImports = removeImports;
9 this.removeVariableFinal = removeVariableFinal;
10 }
11}
diff --git a/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
deleted file mode 100644
index 9e37f16..0000000
--- a/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
+++ /dev/null
@@ -1,108 +0,0 @@
1package cuchaz.enigma.source.cfr;
2
3import com.google.common.io.ByteStreams;
4import cuchaz.enigma.ClassProvider;
5import cuchaz.enigma.source.Decompiler;
6import cuchaz.enigma.source.Source;
7import cuchaz.enigma.source.SourceSettings;
8import org.benf.cfr.reader.apiunreleased.ClassFileSource2;
9import org.benf.cfr.reader.apiunreleased.JarContent;
10import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
11import org.benf.cfr.reader.entities.ClassFile;
12import org.benf.cfr.reader.mapping.MappingFactory;
13import org.benf.cfr.reader.mapping.ObfuscationMapping;
14import org.benf.cfr.reader.relationship.MemberNameResolver;
15import org.benf.cfr.reader.state.DCCommonState;
16import org.benf.cfr.reader.state.TypeUsageCollectingDumper;
17import org.benf.cfr.reader.util.AnalysisType;
18import org.benf.cfr.reader.util.CannotLoadClassException;
19import org.benf.cfr.reader.util.collections.ListFactory;
20import org.benf.cfr.reader.util.getopt.Options;
21import org.benf.cfr.reader.util.getopt.OptionsImpl;
22import org.objectweb.asm.ClassWriter;
23import org.objectweb.asm.tree.ClassNode;
24
25import java.io.IOException;
26import java.io.InputStream;
27import java.util.Collection;
28import java.util.HashMap;
29import java.util.Map;
30
31
32public class CfrDecompiler implements Decompiler {
33 private final DCCommonState state;
34
35 public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) {
36 Map<String, String> options = new HashMap<>();
37
38 state = new DCCommonState(OptionsImpl.getFactory().create(options), new ClassFileSource2() {
39 @Override
40 public JarContent addJarContent(String s, AnalysisType analysisType) {
41 return null;
42 }
43
44 @Override
45 public void informAnalysisRelativePathDetail(String usePath, String classFilePath) {
46
47 }
48
49 @Override
50 public Collection<String> addJar(String jarPath) {
51 return null;
52 }
53
54 @Override
55 public String getPossiblyRenamedPath(String path) {
56 return path;
57 }
58
59 @Override
60 public Pair<byte[], String> getClassFileContent(String path) {
61 ClassNode node = classProvider.getClassNode(path.substring(0, path.lastIndexOf('.')));
62
63 if (node == null) {
64 try (InputStream classResource = CfrDecompiler.class.getClassLoader().getResourceAsStream(path)) {
65 if (classResource != null) {
66 return new Pair<>(ByteStreams.toByteArray(classResource), path);
67 }
68 } catch (IOException ignored) {}
69
70 return null;
71 }
72
73 ClassWriter cw = new ClassWriter(0);
74 node.accept(cw);
75 return new Pair<>(cw.toByteArray(), path);
76 }
77 });
78 }
79
80 @Override
81 public Source getSource(String className) {
82 DCCommonState state = this.state;
83 Options options = state.getOptions();
84
85 ObfuscationMapping mapping = MappingFactory.get(options, state);
86 state = new DCCommonState(state, mapping);
87 ClassFile tree = state.getClassFileMaybePath(className);
88
89 state.configureWith(tree);
90
91 // To make sure we're analysing the cached version
92 try {
93 tree = state.getClassFile(tree.getClassType());
94 } catch (CannotLoadClassException ignored) {}
95
96 if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) {
97 tree.loadInnerClasses(state);
98 }
99
100 if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) {
101 MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes()));
102 }
103
104 TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree);
105 tree.analyseTop(state, typeUsageCollector);
106 return new CfrSource(tree, state, typeUsageCollector.getRealTypeUsageInformation());
107 }
108}
diff --git a/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java b/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
deleted file mode 100644
index d4f2da6..0000000
--- a/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
+++ /dev/null
@@ -1,38 +0,0 @@
1package cuchaz.enigma.source.cfr;
2
3import cuchaz.enigma.source.Source;
4import cuchaz.enigma.source.SourceIndex;
5import cuchaz.enigma.translation.mapping.EntryRemapper;
6import org.benf.cfr.reader.entities.ClassFile;
7import org.benf.cfr.reader.state.DCCommonState;
8import org.benf.cfr.reader.state.TypeUsageInformation;
9
10public class CfrSource implements Source {
11 private final ClassFile tree;
12 private final SourceIndex index;
13 private final String string;
14
15 public CfrSource(ClassFile tree, DCCommonState state, TypeUsageInformation typeUsages) {
16 this.tree = tree;
17
18 EnigmaDumper dumper = new EnigmaDumper(typeUsages);
19 tree.dump(state.getObfuscationMapping().wrap(dumper));
20 index = dumper.getIndex();
21 string = dumper.getString();
22 }
23
24 @Override
25 public String asString() {
26 return string;
27 }
28
29 @Override
30 public Source addJavadocs(EntryRemapper remapper) {
31 return this; // TODO
32 }
33
34 @Override
35 public SourceIndex index() {
36 return index;
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
deleted file mode 100644
index 09e0a9b..0000000
--- a/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
+++ /dev/null
@@ -1,433 +0,0 @@
1package cuchaz.enigma.source.cfr;
2
3import cuchaz.enigma.analysis.Token;
4import cuchaz.enigma.source.SourceIndex;
5import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.TypeDescriptor;
7import cuchaz.enigma.translation.representation.entry.*;
8import org.benf.cfr.reader.bytecode.analysis.types.*;
9import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable;
10import org.benf.cfr.reader.entities.Field;
11import org.benf.cfr.reader.entities.Method;
12import org.benf.cfr.reader.mapping.NullMapping;
13import org.benf.cfr.reader.mapping.ObfuscationMapping;
14import org.benf.cfr.reader.state.TypeUsageInformation;
15import org.benf.cfr.reader.util.collections.SetFactory;
16import org.benf.cfr.reader.util.output.DelegatingDumper;
17import org.benf.cfr.reader.util.output.Dumpable;
18import org.benf.cfr.reader.util.output.Dumper;
19import org.benf.cfr.reader.util.output.TypeContext;
20
21import java.util.Set;
22import java.util.stream.Collectors;
23
24public class EnigmaDumper implements Dumper {
25 private int outputCount = 0;
26 private int indent;
27 private boolean atStart = true;
28 private boolean pendingCR = false;
29 private final StringBuilder sb = new StringBuilder();
30 private final TypeUsageInformation typeUsageInformation;
31 private final Set<JavaTypeInstance> emitted = SetFactory.newSet();
32 private final SourceIndex index = new SourceIndex();
33 private int position;
34
35
36 public EnigmaDumper(TypeUsageInformation typeUsageInformation) {
37 this.typeUsageInformation = typeUsageInformation;
38 }
39
40 private void append(String s) {
41 sb.append(s);
42 position += s.length();
43 }
44
45 private String getDesc(JavaTypeInstance type) {
46 if (!type.isUsableType() && type != RawJavaType.VOID) {
47 throw new IllegalArgumentException(type.toString());
48 }
49
50 if (type instanceof JavaGenericBaseInstance) {
51 return getDesc(type.getDeGenerifiedType());
52 }
53
54 if (type instanceof JavaRefTypeInstance) {
55 return "L" + type.getRawName().replace('.', '/') + ";";
56 }
57
58 if (type instanceof JavaArrayTypeInstance) {
59 return "[" + getDesc(((JavaArrayTypeInstance) type).removeAnArrayIndirection());
60 }
61
62 if (type instanceof RawJavaType) {
63 switch ((RawJavaType) type) {
64 case BOOLEAN:
65 return "Z";
66 case BYTE:
67 return "B";
68 case CHAR:
69 return "C";
70 case SHORT:
71 return "S";
72 case INT:
73 return "I";
74 case LONG:
75 return "J";
76 case FLOAT:
77 return "F";
78 case DOUBLE:
79 return "D";
80 case VOID:
81 return "V";
82 default:
83 throw new AssertionError();
84 }
85 }
86
87 throw new AssertionError();
88 }
89
90 private MethodEntry getMethodEntry(MethodPrototype method) {
91 if (method == null || method.getClassType() == null) {
92 return null;
93 }
94
95 MethodDescriptor desc = new MethodDescriptor(
96 method.getArgs().stream().map(type -> new TypeDescriptor(getDesc(type))).collect(Collectors.toList()),
97 new TypeDescriptor(method.getName().equals("<init>") || method.getName().equals("<clinit>") ? "V" : getDesc(method.getReturnType()))
98 );
99
100 return new MethodEntry(getClassEntry(method.getClassType()), method.getName(), desc);
101 }
102
103 private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) {
104 int variableIndex = method.isInstanceMethod() ? 1 : 0;
105 for (int i = 0; i < parameterIndex; i++) {
106 variableIndex += method.getArgs().get(i).getStackType().getComputationCategory();
107 }
108
109 return new LocalVariableEntry(getMethodEntry(method), variableIndex, name, true, null);
110 }
111
112 private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, JavaTypeInstance type) {
113 return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(getDesc(type)));
114 }
115
116 private ClassEntry getClassEntry(JavaTypeInstance type) {
117 return new ClassEntry(type.getRawName().replace('.', '/'));
118 }
119
120 @Override
121 public Dumper beginBlockComment(boolean inline) {
122 print("/*").newln();
123 return this;
124 }
125
126 @Override
127 public Dumper endBlockComment() {
128 print(" */").newln();
129 return this;
130 }
131
132 @Override
133 public Dumper label(String s, boolean inline) {
134 processPendingCR();
135 append(s);
136 append(":");
137 return this;
138 }
139
140 @Override
141 public Dumper comment(String s) {
142 append("// ");
143 append(s);
144 append("\n");
145 return this;
146 }
147
148 @Override
149 public void enqueuePendingCarriageReturn() {
150 pendingCR = true;
151 }
152
153 @Override
154 public Dumper removePendingCarriageReturn() {
155 pendingCR = false;
156 return this;
157 }
158
159 private void processPendingCR() {
160 if (pendingCR) {
161 append("\n");
162 atStart = true;
163 pendingCR = false;
164 }
165 }
166
167 @Override
168 public Dumper identifier(String s, Object ref, boolean defines) {
169 return print(s);
170 }
171
172 @Override
173 public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) {
174 doIndent();
175 Token token = new Token(position, position + name.length(), name);
176 Entry<?> entry = getMethodEntry(method);
177
178 if (entry != null) {
179 if (defines) {
180 index.addDeclaration(token, entry);
181 } else {
182 index.addReference(token, entry, null);
183 }
184 }
185
186 return identifier(name, null, defines);
187 }
188
189 @Override
190 public Dumper parameterName(String name, MethodPrototype method, int index, boolean defines) {
191 doIndent();
192 Token token = new Token(position, position + name.length(), name);
193 Entry<?> entry = getParameterEntry(method, index, name);
194
195 if (entry != null) {
196 if (defines) {
197 this.index.addDeclaration(token, entry);
198 } else {
199 this.index.addReference(token, entry, null);
200 }
201 }
202
203 return identifier(name, null, defines);
204 }
205
206 @Override
207 public Dumper variableName(String name, NamedVariable variable, boolean defines) {
208 return identifier(name, null, defines);
209 }
210
211 @Override
212 public Dumper packageName(JavaRefTypeInstance t) {
213 String s = t.getPackageName();
214
215 if (!s.isEmpty()) {
216 keyword("package ").print(s).endCodeln().newln();
217 }
218
219 return this;
220 }
221
222 @Override
223 public Dumper fieldName(String name, Field field, JavaTypeInstance owner, boolean hiddenDeclaration, boolean defines) {
224 doIndent();
225 Token token = new Token(position, position + name.length(), name);
226 Entry<?> entry = field == null ? null : getFieldEntry(owner, name, field.getJavaTypeInstance());
227
228 if (entry != null) {
229 if (defines) {
230 index.addDeclaration(token, entry);
231 } else {
232 index.addReference(token, entry, null);
233 }
234 }
235
236 identifier(name, null, defines);
237 return this;
238 }
239
240 @Override
241 public Dumper print(String s) {
242 processPendingCR();
243 doIndent();
244 append(s);
245 atStart = s.endsWith("\n");
246 outputCount++;
247 return this;
248 }
249
250 @Override
251 public Dumper print(char c) {
252 return print(String.valueOf(c));
253 }
254
255 @Override
256 public Dumper newln() {
257 append("\n");
258 atStart = true;
259 outputCount++;
260 return this;
261 }
262
263 @Override
264 public Dumper endCodeln() {
265 append(";\n");
266 atStart = true;
267 outputCount++;
268 return this;
269 }
270
271 @Override
272 public Dumper keyword(String s) {
273 print(s);
274 return this;
275 }
276
277 @Override
278 public Dumper operator(String s) {
279 print(s);
280 return this;
281 }
282
283 @Override
284 public Dumper separator(String s) {
285 print(s);
286 return this;
287 }
288
289 @Override
290 public Dumper literal(String s, Object o) {
291 print(s);
292 return this;
293 }
294
295 private void doIndent() {
296 if (!atStart) return;
297 String indents = " ";
298
299 for (int x = 0; x < indent; ++x) {
300 append(indents);
301 }
302
303 atStart = false;
304 }
305
306 @Override
307 public void indent(int diff) {
308 indent += diff;
309 }
310
311 @Override
312 public Dumper dump(Dumpable d) {
313 if (d == null) {
314 keyword("null");
315 return this;
316 }
317
318 d.dump(this);
319 return this;
320 }
321
322 @Override
323 public TypeUsageInformation getTypeUsageInformation() {
324 return typeUsageInformation;
325 }
326
327 @Override
328 public ObfuscationMapping getObfuscationMapping() {
329 return NullMapping.INSTANCE;
330 }
331
332 @Override
333 public String toString() {
334 return sb.toString();
335 }
336
337 @Override
338 public void addSummaryError(Method method, String s) {}
339
340 @Override
341 public void close() {
342 }
343
344 @Override
345 public boolean canEmitClass(JavaTypeInstance type) {
346 return emitted.add(type);
347 }
348
349 @Override
350 public int getOutputCount() {
351 return outputCount;
352 }
353
354 @Override
355 public Dumper dump(JavaTypeInstance type) {
356 return dump(type, TypeContext.None, false);
357 }
358
359 @Override
360 public Dumper dump(JavaTypeInstance type, boolean defines) {
361 return dump(type, TypeContext.None, false);
362 }
363
364 @Override
365 public Dumper dump(JavaTypeInstance type, TypeContext context) {
366 return dump(type, context, false);
367 }
368
369 private Dumper dump(JavaTypeInstance type, TypeContext context, boolean defines) {
370 doIndent();
371 if (type instanceof JavaRefTypeInstance) {
372 int start = position;
373 type.dumpInto(this, typeUsageInformation, TypeContext.None);
374 int end = position;
375 Token token = new Token(start, end, sb.toString().substring(start, end));
376
377 if (defines) {
378 index.addDeclaration(token, getClassEntry(type));
379 } else {
380 index.addReference(token, getClassEntry(type), null);
381 }
382
383 return this;
384 }
385
386 type.dumpInto(this, typeUsageInformation, context);
387 return this;
388 }
389
390 @Override
391 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
392 return new WithTypeUsageInformationDumper(this, innerclassTypeUsageInformation);
393 }
394
395 public SourceIndex getIndex() {
396 index.setSource(getString());
397 return index;
398 }
399
400 public String getString() {
401 return sb.toString();
402 }
403
404 public static class WithTypeUsageInformationDumper extends DelegatingDumper {
405 private final TypeUsageInformation typeUsageInformation;
406
407 WithTypeUsageInformationDumper(Dumper delegate, TypeUsageInformation typeUsageInformation) {
408 super(delegate);
409 this.typeUsageInformation = typeUsageInformation;
410 }
411
412 @Override
413 public TypeUsageInformation getTypeUsageInformation() {
414 return typeUsageInformation;
415 }
416
417 @Override
418 public Dumper dump(JavaTypeInstance javaTypeInstance) {
419 return dump(javaTypeInstance, TypeContext.None);
420 }
421
422 @Override
423 public Dumper dump(JavaTypeInstance javaTypeInstance, TypeContext typeContext) {
424 javaTypeInstance.dumpInto(this, typeUsageInformation, typeContext);
425 return this;
426 }
427
428 @Override
429 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
430 return new WithTypeUsageInformationDumper(delegate, innerclassTypeUsageInformation);
431 }
432 }
433}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java b/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java
deleted file mode 100644
index 2fae61a..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java
+++ /dev/null
@@ -1,49 +0,0 @@
1package cuchaz.enigma.source.procyon;
2
3import com.strobel.assembler.metadata.FieldDefinition;
4import com.strobel.assembler.metadata.MethodDefinition;
5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference;
7import cuchaz.enigma.translation.representation.AccessFlags;
8import cuchaz.enigma.translation.representation.MethodDescriptor;
9import cuchaz.enigma.translation.representation.Signature;
10import cuchaz.enigma.translation.representation.TypeDescriptor;
11import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
12import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
14import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
15
16public class EntryParser {
17 public static FieldDefEntry parse(FieldDefinition definition) {
18 ClassEntry owner = parse(definition.getDeclaringType());
19 TypeDescriptor descriptor = new TypeDescriptor(definition.getErasedSignature());
20 Signature signature = Signature.createTypedSignature(definition.getSignature());
21 AccessFlags access = new AccessFlags(definition.getModifiers());
22 return new FieldDefEntry(owner, definition.getName(), descriptor, signature, access, null);
23 }
24
25 public static ClassDefEntry parse(TypeDefinition def) {
26 String name = def.getInternalName();
27 Signature signature = Signature.createSignature(def.getSignature());
28 AccessFlags access = new AccessFlags(def.getModifiers());
29 ClassEntry superClass = def.getBaseType() != null ? parse(def.getBaseType()) : null;
30 ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(EntryParser::parse).toArray(ClassEntry[]::new);
31 return new ClassDefEntry(name, signature, access, superClass, interfaces);
32 }
33
34 public static ClassEntry parse(TypeReference typeReference) {
35 return new ClassEntry(typeReference.getInternalName());
36 }
37
38 public static MethodDefEntry parse(MethodDefinition definition) {
39 ClassEntry classEntry = parse(definition.getDeclaringType());
40 MethodDescriptor descriptor = new MethodDescriptor(definition.getErasedSignature());
41 Signature signature = Signature.createSignature(definition.getSignature());
42 AccessFlags access = new AccessFlags(definition.getModifiers());
43 return new MethodDefEntry(classEntry, definition.getName(), descriptor, signature, access, null);
44 }
45
46 public static TypeDescriptor parseTypeDescriptor(TypeReference type) {
47 return new TypeDescriptor(type.getErasedSignature());
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java b/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
deleted file mode 100644
index 37bc0c8..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
+++ /dev/null
@@ -1,81 +0,0 @@
1package cuchaz.enigma.source.procyon;
2
3import com.strobel.assembler.metadata.ITypeLoader;
4import com.strobel.assembler.metadata.MetadataSystem;
5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference;
7import com.strobel.decompiler.DecompilerContext;
8import com.strobel.decompiler.DecompilerSettings;
9import com.strobel.decompiler.languages.java.BraceStyle;
10import com.strobel.decompiler.languages.java.JavaFormattingOptions;
11import com.strobel.decompiler.languages.java.ast.AstBuilder;
12import com.strobel.decompiler.languages.java.ast.CompilationUnit;
13import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
14import cuchaz.enigma.ClassProvider;
15import cuchaz.enigma.api.EnigmaPluginContext;
16import cuchaz.enigma.source.Source;
17import cuchaz.enigma.source.Decompiler;
18import cuchaz.enigma.source.SourceSettings;
19import cuchaz.enigma.source.procyon.transformers.*;
20import cuchaz.enigma.source.procyon.typeloader.CompiledSourceTypeLoader;
21import cuchaz.enigma.source.procyon.typeloader.NoRetryMetadataSystem;
22import cuchaz.enigma.source.procyon.typeloader.SynchronizedTypeLoader;
23import cuchaz.enigma.utils.Utils;
24
25public class ProcyonDecompiler implements Decompiler {
26 private final SourceSettings settings;
27 private final DecompilerSettings decompilerSettings;
28 private final MetadataSystem metadataSystem;
29
30 public ProcyonDecompiler(ClassProvider classProvider, SourceSettings settings) {
31 ITypeLoader typeLoader = new SynchronizedTypeLoader(new CompiledSourceTypeLoader(classProvider));
32
33 metadataSystem = new NoRetryMetadataSystem(typeLoader);
34 metadataSystem.setEagerMethodLoadingEnabled(true);
35
36 decompilerSettings = DecompilerSettings.javaDefaults();
37 decompilerSettings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
38 decompilerSettings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
39 decompilerSettings.setForceExplicitTypeArguments(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
40 decompilerSettings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
41 decompilerSettings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
42 decompilerSettings.setTypeLoader(typeLoader);
43
44 JavaFormattingOptions formattingOptions = decompilerSettings.getJavaFormattingOptions();
45 formattingOptions.ClassBraceStyle = BraceStyle.EndOfLine;
46 formattingOptions.InterfaceBraceStyle = BraceStyle.EndOfLine;
47 formattingOptions.EnumBraceStyle = BraceStyle.EndOfLine;
48
49 this.settings = settings;
50 }
51
52 @Override
53 public Source getSource(String className) {
54 TypeReference type = metadataSystem.lookupType(className);
55 if (type == null) {
56 throw new Error(String.format("Unable to find desc: %s", className));
57 }
58
59 TypeDefinition resolvedType = type.resolve();
60
61 DecompilerContext context = new DecompilerContext();
62 context.setCurrentType(resolvedType);
63 context.setSettings(decompilerSettings);
64
65 AstBuilder builder = new AstBuilder(context);
66 builder.addType(resolvedType);
67 builder.runTransformations(null);
68 CompilationUnit source = builder.getCompilationUnit();
69
70 new ObfuscatedEnumSwitchRewriterTransform(context).run(source);
71 new VarargsFixer(context).run(source);
72 new RemoveObjectCasts(context).run(source);
73 new Java8Generics().run(source);
74 new InvalidIdentifierFix().run(source);
75 if (settings.removeImports) DropImportAstTransform.INSTANCE.run(source);
76 if (settings.removeVariableFinal) DropVarModifiersAstTransform.INSTANCE.run(source);
77 source.acceptVisitor(new InsertParenthesesVisitor(), null);
78
79 return new ProcyonSource(source, decompilerSettings);
80 }
81}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java b/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
deleted file mode 100644
index 53c8c70..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
+++ /dev/null
@@ -1,49 +0,0 @@
1package cuchaz.enigma.source.procyon;
2
3import com.strobel.decompiler.DecompilerSettings;
4import com.strobel.decompiler.PlainTextOutput;
5import com.strobel.decompiler.languages.java.JavaOutputVisitor;
6import com.strobel.decompiler.languages.java.ast.CompilationUnit;
7import cuchaz.enigma.source.Source;
8import cuchaz.enigma.source.SourceIndex;
9import cuchaz.enigma.source.procyon.index.SourceIndexVisitor;
10import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform;
11import cuchaz.enigma.translation.mapping.EntryRemapper;
12
13import java.io.StringWriter;
14
15public class ProcyonSource implements Source {
16 private final DecompilerSettings settings;
17 private final CompilationUnit tree;
18 private String string;
19
20 public ProcyonSource(CompilationUnit tree, DecompilerSettings settings) {
21 this.settings = settings;
22 this.tree = tree;
23 }
24
25 @Override
26 public SourceIndex index() {
27 SourceIndex index = new SourceIndex(asString());
28 tree.acceptVisitor(new SourceIndexVisitor(), index);
29 return index;
30 }
31
32 @Override
33 public String asString() {
34 if (string == null) {
35 StringWriter writer = new StringWriter();
36 tree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null);
37 string = writer.toString();
38 }
39
40 return string;
41 }
42
43 @Override
44 public Source addJavadocs(EntryRemapper remapper) {
45 CompilationUnit remappedTree = (CompilationUnit) tree.clone();
46 new AddJavadocsAstTransform(remapper).run(remappedTree);
47 return new ProcyonSource(remappedTree, settings);
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java
deleted file mode 100644
index f6eeb15..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java
+++ /dev/null
@@ -1,95 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.source.procyon.index;
13
14import com.strobel.assembler.metadata.FieldDefinition;
15import com.strobel.assembler.metadata.MethodDefinition;
16import com.strobel.assembler.metadata.TypeDefinition;
17import com.strobel.assembler.metadata.TypeReference;
18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.source.SourceIndex;
21import cuchaz.enigma.source.procyon.EntryParser;
22import cuchaz.enigma.translation.representation.entry.*;
23
24public class SourceIndexClassVisitor extends SourceIndexVisitor {
25 private ClassDefEntry classEntry;
26
27 public SourceIndexClassVisitor(ClassDefEntry classEntry) {
28 this.classEntry = classEntry;
29 }
30
31 @Override
32 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
33 // is this this class, or a subtype?
34 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
35 ClassDefEntry classEntry = EntryParser.parse(def);
36 if (!classEntry.equals(this.classEntry)) {
37 // it's a subtype, recurse
38 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), classEntry);
39 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
40 }
41
42 return visitChildren(node, index);
43 }
44
45 @Override
46 public Void visitSimpleType(SimpleType node, SourceIndex index) {
47 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
48 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
49 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
50 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.classEntry);
51 }
52
53 return visitChildren(node, index);
54 }
55
56 @Override
57 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
58 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
59 MethodDefEntry methodEntry = EntryParser.parse(def);
60 AstNode tokenNode = node.getNameToken();
61 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) {
62 // for static initializers, check elsewhere for the token node
63 tokenNode = node.getModifiers().firstOrNullObject();
64 }
65 index.addDeclaration(TokenFactory.createToken(index, tokenNode), methodEntry);
66 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
67 }
68
69 @Override
70 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
71 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
72 MethodDefEntry methodEntry = EntryParser.parse(def);
73 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), methodEntry);
74 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
75 }
76
77 @Override
78 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
79 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
80 FieldDefEntry fieldEntry = EntryParser.parse(def);
81 assert (node.getVariables().size() == 1);
82 VariableInitializer variable = node.getVariables().firstOrNullObject();
83 index.addDeclaration(TokenFactory.createToken(index, variable.getNameToken()), fieldEntry);
84 return visitChildren(node, index);
85 }
86
87 @Override
88 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
89 // treat enum declarations as field declarations
90 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
91 FieldDefEntry fieldEntry = EntryParser.parse(def);
92 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), fieldEntry);
93 return visitChildren(node, index);
94 }
95}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java
deleted file mode 100644
index 0e8bc51..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java
+++ /dev/null
@@ -1,218 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.source.procyon.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import com.strobel.assembler.metadata.*;
17import com.strobel.decompiler.ast.Variable;
18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.source.SourceIndex;
21import cuchaz.enigma.source.procyon.EntryParser;
22import cuchaz.enigma.translation.representation.MethodDescriptor;
23import cuchaz.enigma.translation.representation.TypeDescriptor;
24import cuchaz.enigma.translation.representation.entry.*;
25
26import java.lang.Error;
27import java.util.HashMap;
28import java.util.Map;
29
30public class SourceIndexMethodVisitor extends SourceIndexVisitor {
31 private final MethodDefEntry methodEntry;
32
33 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
34 private Map<String, Entry<?>> identifierEntryCache = new HashMap<>();
35
36 public SourceIndexMethodVisitor(MethodDefEntry methodEntry) {
37 this.methodEntry = methodEntry;
38 }
39
40 @Override
41 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
42 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
43
44 // get the behavior entry
45 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
46 MethodEntry methodEntry = null;
47 if (ref instanceof MethodReference) {
48 methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature()));
49 }
50 if (methodEntry != null) {
51 // get the node for the token
52 AstNode tokenNode = null;
53 if (node.getTarget() instanceof MemberReferenceExpression) {
54 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken();
55 } else if (node.getTarget() instanceof SuperReferenceExpression) {
56 tokenNode = node.getTarget();
57 } else if (node.getTarget() instanceof ThisReferenceExpression) {
58 tokenNode = node.getTarget();
59 }
60 if (tokenNode != null) {
61 index.addReference(TokenFactory.createToken(index, tokenNode), methodEntry, this.methodEntry);
62 }
63 }
64
65 // Check for identifier
66 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
67 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
68 return visitChildren(node, index);
69 }
70
71 @Override
72 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
73 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
74 if (ref instanceof FieldReference) {
75 // make sure this is actually a field
76 String erasedSignature = ref.getErasedSignature();
77 if (erasedSignature.indexOf('(') >= 0) {
78 throw new Error("Expected a field here! got " + ref);
79 }
80
81 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
82 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
83 index.addReference(TokenFactory.createToken(index, node.getMemberNameToken()), fieldEntry, this.methodEntry);
84 }
85
86 return visitChildren(node, index);
87 }
88
89 @Override
90 public Void visitSimpleType(SimpleType node, SourceIndex index) {
91 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
92 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
93 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
94 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.methodEntry);
95 }
96
97 return visitChildren(node, index);
98 }
99
100 @Override
101 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
102 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
103 int parameterIndex = def.getSlot();
104
105 if (parameterIndex >= 0) {
106 MethodDefEntry ownerMethod = methodEntry;
107 if (def.getMethod() instanceof MethodDefinition) {
108 ownerMethod = EntryParser.parse((MethodDefinition) def.getMethod());
109 }
110
111 TypeDescriptor parameterType = EntryParser.parseTypeDescriptor(def.getParameterType());
112 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, parameterIndex, node.getName(), true, parameterType, null);
113 Identifier identifier = node.getNameToken();
114 // cache the argument entry and the identifier
115 identifierEntryCache.put(identifier.getName(), localVariableEntry);
116 index.addDeclaration(TokenFactory.createToken(index, identifier), localVariableEntry);
117 }
118
119 return visitChildren(node, index);
120 }
121
122 @Override
123 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
124 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
125 if (ref != null) {
126 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
127 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
128 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), fieldEntry, this.methodEntry);
129 } else
130 this.checkIdentifier(node, index);
131 return visitChildren(node, index);
132 }
133
134 private void checkIdentifier(IdentifierExpression node, SourceIndex index) {
135 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token!
136 index.addDeclaration(TokenFactory.createToken(index, node.getIdentifierToken()), identifierEntryCache.get(node.getIdentifier()));
137 else
138 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it!
139 }
140
141 private void addDeclarationToUnmatched(String key, SourceIndex index) {
142 Entry<?> entry = identifierEntryCache.get(key);
143
144 // This cannot happened in theory
145 if (entry == null)
146 return;
147 for (Identifier identifier : unmatchedIdentifier.get(key))
148 index.addDeclaration(TokenFactory.createToken(index, identifier), entry);
149 unmatchedIdentifier.removeAll(key);
150 }
151
152 @Override
153 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
154 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
155 if (ref != null && node.getType() instanceof SimpleType) {
156 SimpleType simpleTypeNode = (SimpleType) node.getType();
157 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
158 MethodEntry constructorEntry = new MethodEntry(classEntry, "<init>", new MethodDescriptor(ref.getErasedSignature()));
159 index.addReference(TokenFactory.createToken(index, simpleTypeNode.getIdentifierToken()), constructorEntry, this.methodEntry);
160 }
161
162 return visitChildren(node, index);
163 }
164
165 @Override
166 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
167 AstNodeCollection<VariableInitializer> variables = node.getVariables();
168
169 // Single assignation
170 if (variables.size() == 1) {
171 VariableInitializer initializer = variables.firstOrNullObject();
172 if (initializer != null && node.getType() instanceof SimpleType) {
173 Identifier identifier = initializer.getNameToken();
174 Variable variable = initializer.getUserData(Keys.VARIABLE);
175 if (variable != null) {
176 VariableDefinition originalVariable = variable.getOriginalVariable();
177 if (originalVariable != null) {
178 int variableIndex = originalVariable.getSlot();
179 if (variableIndex >= 0) {
180 MethodDefEntry ownerMethod = EntryParser.parse(originalVariable.getDeclaringMethod());
181 TypeDescriptor variableType = EntryParser.parseTypeDescriptor(originalVariable.getVariableType());
182 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, variableIndex, initializer.getName(), false, variableType, null);
183 identifierEntryCache.put(identifier.getName(), localVariableEntry);
184 addDeclarationToUnmatched(identifier.getName(), index);
185 index.addDeclaration(TokenFactory.createToken(index, identifier), localVariableEntry);
186 }
187 }
188 }
189 }
190 }
191 return visitChildren(node, index);
192 }
193
194 @Override
195 public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) {
196 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
197
198 if (ref instanceof MethodReference) {
199 // get the behavior entry
200 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
201 MethodEntry methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature()));
202
203 // get the node for the token
204 AstNode methodNameToken = node.getMethodNameToken();
205 AstNode targetToken = node.getTarget();
206
207 if (methodNameToken != null) {
208 index.addReference(TokenFactory.createToken(index, methodNameToken), methodEntry, this.methodEntry);
209 }
210
211 if (targetToken != null && !(targetToken instanceof ThisReferenceExpression)) {
212 index.addReference(TokenFactory.createToken(index, targetToken), methodEntry.getParent(), this.methodEntry);
213 }
214 }
215
216 return visitChildren(node, index);
217 }
218}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java
deleted file mode 100644
index dad505f..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java
+++ /dev/null
@@ -1,40 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.source.procyon.index;
13
14import com.strobel.assembler.metadata.TypeDefinition;
15import com.strobel.decompiler.languages.java.ast.AstNode;
16import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
17import com.strobel.decompiler.languages.java.ast.Keys;
18import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
19import cuchaz.enigma.source.SourceIndex;
20import cuchaz.enigma.source.procyon.EntryParser;
21import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
22
23public class SourceIndexVisitor extends DepthFirstAstVisitor<SourceIndex, Void> {
24 @Override
25 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
26 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
27 ClassDefEntry classEntry = EntryParser.parse(def);
28 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), classEntry);
29
30 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
31 }
32
33 @Override
34 protected Void visitChildren(AstNode node, SourceIndex index) {
35 for (final AstNode child : node.getChildren()) {
36 child.acceptVisitor(this, index);
37 }
38 return null;
39 }
40}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java b/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java
deleted file mode 100644
index 62e7c10..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
1package cuchaz.enigma.source.procyon.index;
2
3import com.strobel.decompiler.languages.Region;
4import com.strobel.decompiler.languages.java.ast.AstNode;
5import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
6import com.strobel.decompiler.languages.java.ast.Identifier;
7import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
8import cuchaz.enigma.analysis.Token;
9import cuchaz.enigma.source.SourceIndex;
10
11import java.util.regex.Pattern;
12
13public class TokenFactory {
14 private static final Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
15
16 public static Token createToken(SourceIndex index, AstNode node) {
17 String name = node instanceof Identifier ? ((Identifier) node).getName() : "";
18 Region region = node.getRegion();
19
20 if (region.getBeginLine() == 0) {
21 System.err.println("Got bad region from Procyon for node " + node);
22 return null;
23 }
24
25 int start = index.getPosition(region.getBeginLine(), region.getBeginColumn());
26 int end = index.getPosition(region.getEndLine(), region.getEndColumn());
27 String text = index.getSource().substring(start, end);
28 Token token = new Token(start, end, text);
29
30 boolean isAnonymousInner =
31 node instanceof Identifier &&
32 name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration &&
33 name.lastIndexOf('$') >= 0 &&
34 !ANONYMOUS_INNER.matcher(name).matches();
35
36 if (isAnonymousInner) {
37 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
38 if (type != null) {
39 name = type.getName();
40 token.end = token.start + name.length();
41 }
42 }
43
44 return token;
45 }
46}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
deleted file mode 100644
index 70fc8c6..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
+++ /dev/null
@@ -1,134 +0,0 @@
1package cuchaz.enigma.source.procyon.transformers;
2
3import com.google.common.base.Function;
4import com.google.common.base.Strings;
5import com.strobel.assembler.metadata.ParameterDefinition;
6import com.strobel.decompiler.languages.java.ast.*;
7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
8import cuchaz.enigma.source.procyon.EntryParser;
9import cuchaz.enigma.translation.mapping.EntryMapping;
10import cuchaz.enigma.translation.mapping.EntryRemapper;
11import cuchaz.enigma.translation.mapping.ResolutionStrategy;
12import cuchaz.enigma.translation.representation.entry.*;
13
14import java.util.ArrayList;
15import java.util.Collections;
16import java.util.List;
17import java.util.Objects;
18import java.util.stream.Stream;
19
20public final class AddJavadocsAstTransform implements IAstTransform {
21
22 private final EntryRemapper remapper;
23
24 public AddJavadocsAstTransform(EntryRemapper remapper) {
25 this.remapper = remapper;
26 }
27
28 @Override
29 public void run(AstNode compilationUnit) {
30 compilationUnit.acceptVisitor(new Visitor(remapper), null);
31 }
32
33 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
34
35 private final EntryRemapper remapper;
36
37 Visitor(EntryRemapper remapper) {
38 this.remapper = remapper;
39 }
40
41 private <T extends AstNode> void addDoc(T node, Function<T, Entry<?>> retriever) {
42 final Comment[] comments = getComments(node, retriever);
43 if (comments != null) {
44 node.insertChildrenBefore(node.getFirstChild(), Roles.COMMENT, comments);
45 }
46 }
47
48 private <T extends AstNode> Comment[] getComments(T node, Function<T, Entry<?>> retriever) {
49 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node));
50 final String docs = mapping == null ? null : Strings.emptyToNull(mapping.getJavadoc());
51 return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st,
52 CommentType.Documentation)).toArray(Comment[]::new);
53 }
54
55 private Comment[] getParameterComments(ParameterDeclaration node, Function<ParameterDeclaration, Entry<?>> retriever) {
56 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node));
57 final Comment[] ret = getComments(node, retriever);
58 if (ret != null) {
59 final String paramPrefix = "@param " + mapping.getTargetName() + " ";
60 final String indent = Strings.repeat(" ", paramPrefix.length());
61 ret[0].setContent(paramPrefix + ret[0].getContent());
62 for (int i = 1; i < ret.length; i++) {
63 ret[i].setContent(indent + ret[i].getContent());
64 }
65 }
66 return ret;
67 }
68
69 private void visitMethod(AstNode node) {
70 final MethodDefEntry methodDefEntry = EntryParser.parse(node.getUserData(Keys.METHOD_DEFINITION));
71 final Comment[] baseComments = getComments(node, $ -> methodDefEntry);
72 List<Comment> comments = new ArrayList<>();
73 if (baseComments != null)
74 Collections.addAll(comments, baseComments);
75
76 for (ParameterDeclaration dec : node.getChildrenByRole(Roles.PARAMETER)) {
77 ParameterDefinition def = dec.getUserData(Keys.PARAMETER_DEFINITION);
78 final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(),
79 true,
80 EntryParser.parseTypeDescriptor(def.getParameterType()), null));
81 if (paramComments != null)
82 Collections.addAll(comments, paramComments);
83 }
84
85 if (!comments.isEmpty()) {
86 if (remapper.getObfResolver().resolveEntry(methodDefEntry, ResolutionStrategy.RESOLVE_ROOT).stream().noneMatch(e -> Objects.equals(e, methodDefEntry))) {
87 comments.add(0, new Comment("{@inheritDoc}", CommentType.Documentation));
88 }
89 final AstNode oldFirst = node.getFirstChild();
90 for (Comment comment : comments) {
91 node.insertChildBefore(oldFirst, comment, Roles.COMMENT);
92 }
93 }
94 }
95
96 @Override
97 protected Void visitChildren(AstNode node, Void data) {
98 for (final AstNode child : node.getChildren()) {
99 child.acceptVisitor(this, data);
100 }
101 return null;
102 }
103
104 @Override
105 public Void visitMethodDeclaration(MethodDeclaration node, Void data) {
106 visitMethod(node);
107 return super.visitMethodDeclaration(node, data);
108 }
109
110 @Override
111 public Void visitConstructorDeclaration(ConstructorDeclaration node, Void data) {
112 visitMethod(node);
113 return super.visitConstructorDeclaration(node, data);
114 }
115
116 @Override
117 public Void visitFieldDeclaration(FieldDeclaration node, Void data) {
118 addDoc(node, dec -> EntryParser.parse(dec.getUserData(Keys.FIELD_DEFINITION)));
119 return super.visitFieldDeclaration(node, data);
120 }
121
122 @Override
123 public Void visitTypeDeclaration(TypeDeclaration node, Void data) {
124 addDoc(node, dec -> EntryParser.parse(dec.getUserData(Keys.TYPE_DEFINITION)));
125 return super.visitTypeDeclaration(node, data);
126 }
127
128 @Override
129 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void data) {
130 addDoc(node, dec -> EntryParser.parse(dec.getUserData(Keys.FIELD_DEFINITION)));
131 return super.visitEnumValueDeclaration(node, data);
132 }
133 }
134}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/DropImportAstTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/DropImportAstTransform.java
deleted file mode 100644
index 39e599d..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/DropImportAstTransform.java
+++ /dev/null
@@ -1,33 +0,0 @@
1package cuchaz.enigma.source.procyon.transformers;
2
3import com.strobel.decompiler.languages.java.ast.AstNode;
4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
5import com.strobel.decompiler.languages.java.ast.ImportDeclaration;
6import com.strobel.decompiler.languages.java.ast.PackageDeclaration;
7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
8
9public final class DropImportAstTransform implements IAstTransform {
10 public static final DropImportAstTransform INSTANCE = new DropImportAstTransform();
11
12 private DropImportAstTransform() {
13 }
14
15 @Override
16 public void run(AstNode compilationUnit) {
17 compilationUnit.acceptVisitor(new Visitor(), null);
18 }
19
20 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
21 @Override
22 public Void visitPackageDeclaration(PackageDeclaration node, Void data) {
23 node.remove();
24 return null;
25 }
26
27 @Override
28 public Void visitImportDeclaration(ImportDeclaration node, Void data) {
29 node.remove();
30 return null;
31 }
32 }
33}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java
deleted file mode 100644
index b8c087b..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java
+++ /dev/null
@@ -1,37 +0,0 @@
1package cuchaz.enigma.source.procyon.transformers;
2
3import com.strobel.decompiler.languages.java.ast.*;
4import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
5
6import javax.lang.model.element.Modifier;
7
8public final class DropVarModifiersAstTransform implements IAstTransform {
9 public static final DropVarModifiersAstTransform INSTANCE = new DropVarModifiersAstTransform();
10
11 private DropVarModifiersAstTransform() {
12 }
13
14 @Override
15 public void run(AstNode compilationUnit) {
16 compilationUnit.acceptVisitor(new Visitor(), null);
17 }
18
19 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
20 @Override
21 public Void visitParameterDeclaration(ParameterDeclaration node, Void data) {
22 for (JavaModifierToken modifierToken : node.getChildrenByRole(EntityDeclaration.MODIFIER_ROLE)) {
23 if (modifierToken.getModifier() == Modifier.FINAL) {
24 modifierToken.remove();
25 }
26 }
27
28 return null;
29 }
30
31 @Override
32 public Void visitVariableDeclaration(VariableDeclarationStatement node, Void data) {
33 node.removeModifier(Modifier.FINAL);
34 return null;
35 }
36 }
37}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java
deleted file mode 100644
index 34d95fa..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java
+++ /dev/null
@@ -1,29 +0,0 @@
1package cuchaz.enigma.source.procyon.transformers;
2
3import com.strobel.decompiler.languages.java.ast.AstNode;
4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
5import com.strobel.decompiler.languages.java.ast.Identifier;
6import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
7
8/**
9 * Created by Thiakil on 13/07/2018.
10 */
11public class InvalidIdentifierFix implements IAstTransform {
12 @Override
13 public void run(AstNode compilationUnit) {
14 compilationUnit.acceptVisitor(new Visitor(), null);
15 }
16
17 class Visitor extends DepthFirstAstVisitor<Void,Void>{
18 @Override
19 public Void visitIdentifier(Identifier node, Void data) {
20 super.visitIdentifier(node, data);
21 if (node.getName().equals("do") || node.getName().equals("if")){
22 Identifier newIdentifier = Identifier.create(node.getName() + "_", node.getStartLocation());
23 newIdentifier.copyUserDataFrom(node);
24 node.replaceWith(newIdentifier);
25 }
26 return null;
27 }
28 }
29}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
deleted file mode 100644
index 8accfc7..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
+++ /dev/null
@@ -1,107 +0,0 @@
1package cuchaz.enigma.source.procyon.transformers;
2
3import com.strobel.assembler.metadata.BuiltinTypes;
4import com.strobel.assembler.metadata.CommonTypeReferences;
5import com.strobel.assembler.metadata.Flags;
6import com.strobel.assembler.metadata.IGenericInstance;
7import com.strobel.assembler.metadata.IMemberDefinition;
8import com.strobel.assembler.metadata.JvmType;
9import com.strobel.assembler.metadata.MemberReference;
10import com.strobel.assembler.metadata.MethodDefinition;
11import com.strobel.assembler.metadata.TypeDefinition;
12import com.strobel.assembler.metadata.TypeReference;
13import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression;
14import com.strobel.decompiler.languages.java.ast.AstNode;
15import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
16import com.strobel.decompiler.languages.java.ast.AstType;
17import com.strobel.decompiler.languages.java.ast.CastExpression;
18import com.strobel.decompiler.languages.java.ast.ComposedType;
19import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
20import com.strobel.decompiler.languages.java.ast.Expression;
21import com.strobel.decompiler.languages.java.ast.Identifier;
22import com.strobel.decompiler.languages.java.ast.InvocationExpression;
23import com.strobel.decompiler.languages.java.ast.Keys;
24import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
25import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
26import com.strobel.decompiler.languages.java.ast.Roles;
27import com.strobel.decompiler.languages.java.ast.SimpleType;
28import com.strobel.decompiler.languages.java.ast.WildcardType;
29import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
30
31/**
32 * Created by Thiakil on 12/07/2018.
33 */
34public class Java8Generics implements IAstTransform {
35
36 @Override
37 public void run(AstNode compilationUnit) {
38 compilationUnit.acceptVisitor(new Visitor(), null);
39 }
40
41 static class Visitor extends DepthFirstAstVisitor<Void,Void>{
42
43 @Override
44 public Void visitInvocationExpression(InvocationExpression node, Void data) {
45 super.visitInvocationExpression(node, data);
46 if (node.getTarget() instanceof MemberReferenceExpression){
47 MemberReferenceExpression referenceExpression = (MemberReferenceExpression) node.getTarget();
48 if (referenceExpression.getTypeArguments().stream().map(t->{
49 TypeReference tr = t.toTypeReference();
50 if (tr.getDeclaringType() != null){//ensure that inner types are resolved so we can get the TypeDefinition below
51 TypeReference resolved = tr.resolve();
52 if (resolved != null)
53 return resolved;
54 }
55 return tr;
56 }).anyMatch(t -> t.isWildcardType() || (t instanceof TypeDefinition && ((TypeDefinition) t).isAnonymous()))) {
57 //these are invalid for invocations, let the compiler work it out
58 referenceExpression.getTypeArguments().clear();
59 } else if (referenceExpression.getTypeArguments().stream().allMatch(t->t.toTypeReference().equals(CommonTypeReferences.Object))){
60 //all are <Object>, thereby redundant and/or bad
61 referenceExpression.getTypeArguments().clear();
62 }
63 }
64 return null;
65 }
66
67 @Override
68 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) {
69 super.visitObjectCreationExpression(node, data);
70 AstType type = node.getType();
71 if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()){
72 SimpleType simpleType = (SimpleType) type;
73 AstNodeCollection<AstType> typeArguments = simpleType.getTypeArguments();
74 if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)){
75 //all are <Object>, thereby redundant and/or bad
76 typeArguments.firstOrNullObject().getChildByRole(Roles.IDENTIFIER).replaceWith(Identifier.create(""));
77 }
78 }
79 return null;
80 }
81
82 @Override
83 public Void visitCastExpression(CastExpression node, Void data) {
84 boolean doReplace = false;
85 TypeReference typeReference = node.getType().toTypeReference();
86 if (typeReference.isArray() && typeReference.getElementType().isGenericType()){
87 doReplace = true;
88 } else if (typeReference.isGenericType()) {
89 Expression target = node.getExpression();
90 if (typeReference instanceof IGenericInstance && ((IGenericInstance)typeReference).getTypeArguments().stream().anyMatch(t->t.isWildcardType())){
91 doReplace = true;
92 } else if (target instanceof InvocationExpression) {
93 InvocationExpression invocationExpression = (InvocationExpression)target;
94 if (invocationExpression.getTarget() instanceof MemberReferenceExpression && !((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().isEmpty()) {
95 ((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().clear();
96 doReplace = true;
97 }
98 }
99 }
100 super.visitCastExpression(node, data);
101 if (doReplace){
102 node.replaceWith(node.getExpression());
103 }
104 return null;
105 }
106 }
107}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java
deleted file mode 100644
index 32bb72f..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java
+++ /dev/null
@@ -1,414 +0,0 @@
1/*
2 * Originally:
3 * EnumSwitchRewriterTransform.java
4 *
5 * Copyright (c) 2013 Mike Strobel
6 *
7 * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain;
8 * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa.
9 *
10 * This source code is subject to terms and conditions of the Apache License, Version 2.0.
11 * A copy of the license can be found in the License.html file at the root of this distribution.
12 * By using this source code in any fashion, you are agreeing to be bound by the terms of the
13 * Apache License, Version 2.0.
14 *
15 * You must not remove this notice, or any other, from this software.
16 */
17
18package cuchaz.enigma.source.procyon.transformers;
19
20import com.strobel.assembler.metadata.BuiltinTypes;
21import com.strobel.assembler.metadata.FieldDefinition;
22import com.strobel.assembler.metadata.MethodDefinition;
23import com.strobel.assembler.metadata.TypeDefinition;
24import com.strobel.assembler.metadata.TypeReference;
25import com.strobel.core.SafeCloseable;
26import com.strobel.core.VerifyArgument;
27import com.strobel.decompiler.DecompilerContext;
28import com.strobel.decompiler.languages.java.ast.AssignmentExpression;
29import com.strobel.decompiler.languages.java.ast.AstBuilder;
30import com.strobel.decompiler.languages.java.ast.AstNode;
31import com.strobel.decompiler.languages.java.ast.CaseLabel;
32import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor;
33import com.strobel.decompiler.languages.java.ast.Expression;
34import com.strobel.decompiler.languages.java.ast.IdentifierExpression;
35import com.strobel.decompiler.languages.java.ast.IndexerExpression;
36import com.strobel.decompiler.languages.java.ast.InvocationExpression;
37import com.strobel.decompiler.languages.java.ast.Keys;
38import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
39import com.strobel.decompiler.languages.java.ast.PrimitiveExpression;
40import com.strobel.decompiler.languages.java.ast.SwitchSection;
41import com.strobel.decompiler.languages.java.ast.SwitchStatement;
42import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
43import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression;
44import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
45
46import java.util.ArrayList;
47import java.util.IdentityHashMap;
48import java.util.LinkedHashMap;
49import java.util.List;
50import java.util.Map;
51
52/**
53 * Copy of {@link com.strobel.decompiler.languages.java.ast.transforms.EnumSwitchRewriterTransform} modified to:
54 * - Not rely on a field containing "$SwitchMap$" (Proguard strips it)
55 * - Ignore classes *with* SwitchMap$ names (so the original can handle it)
56 * - Ignores inner synthetics that are not package private
57 */
58@SuppressWarnings("Duplicates")
59public class ObfuscatedEnumSwitchRewriterTransform implements IAstTransform {
60 private final DecompilerContext _context;
61
62 public ObfuscatedEnumSwitchRewriterTransform(final DecompilerContext context) {
63 _context = VerifyArgument.notNull(context, "context");
64 }
65
66 @Override
67 public void run(final AstNode compilationUnit) {
68 compilationUnit.acceptVisitor(new Visitor(_context), null);
69 }
70
71 private final static class Visitor extends ContextTrackingVisitor<Void> {
72 private final static class SwitchMapInfo {
73 final String enclosingType;
74 final Map<String, List<SwitchStatement>> switches = new LinkedHashMap<>();
75 final Map<String, Map<Integer, Expression>> mappings = new LinkedHashMap<>();
76
77 TypeDeclaration enclosingTypeDeclaration;
78
79 SwitchMapInfo(final String enclosingType) {
80 this.enclosingType = enclosingType;
81 }
82 }
83
84 private final Map<String, SwitchMapInfo> _switchMaps = new LinkedHashMap<>();
85 private boolean _isSwitchMapWrapper;
86
87 protected Visitor(final DecompilerContext context) {
88 super(context);
89 }
90
91 @Override
92 public Void visitTypeDeclaration(final TypeDeclaration typeDeclaration, final Void p) {
93 final boolean oldIsSwitchMapWrapper = _isSwitchMapWrapper;
94 final TypeDefinition typeDefinition = typeDeclaration.getUserData(Keys.TYPE_DEFINITION);
95 final boolean isSwitchMapWrapper = isSwitchMapWrapper(typeDefinition);
96
97 if (isSwitchMapWrapper) {
98 final String internalName = typeDefinition.getInternalName();
99
100 SwitchMapInfo info = _switchMaps.get(internalName);
101
102 if (info == null) {
103 _switchMaps.put(internalName, info = new SwitchMapInfo(internalName));
104 }
105
106 info.enclosingTypeDeclaration = typeDeclaration;
107 }
108
109 _isSwitchMapWrapper = isSwitchMapWrapper;
110
111 try {
112 super.visitTypeDeclaration(typeDeclaration, p);
113 }
114 finally {
115 _isSwitchMapWrapper = oldIsSwitchMapWrapper;
116 }
117
118 rewrite();
119
120 return null;
121 }
122
123 @Override
124 public Void visitSwitchStatement(final SwitchStatement node, final Void data) {
125 final Expression test = node.getExpression();
126
127 if (test instanceof IndexerExpression) {
128 final IndexerExpression indexer = (IndexerExpression) test;
129 final Expression array = indexer.getTarget();
130 final Expression argument = indexer.getArgument();
131
132 if (!(array instanceof MemberReferenceExpression)) {
133 return super.visitSwitchStatement(node, data);
134 }
135
136 final MemberReferenceExpression arrayAccess = (MemberReferenceExpression) array;
137 final Expression arrayOwner = arrayAccess.getTarget();
138 final String mapName = arrayAccess.getMemberName();
139
140 if (mapName == null || mapName.startsWith("$SwitchMap$") || !(arrayOwner instanceof TypeReferenceExpression)) {
141 return super.visitSwitchStatement(node, data);
142 }
143
144 final TypeReferenceExpression enclosingTypeExpression = (TypeReferenceExpression) arrayOwner;
145 final TypeReference enclosingType = enclosingTypeExpression.getType().getUserData(Keys.TYPE_REFERENCE);
146
147 if (!isSwitchMapWrapper(enclosingType) || !(argument instanceof InvocationExpression)) {
148 return super.visitSwitchStatement(node, data);
149 }
150
151 final InvocationExpression invocation = (InvocationExpression) argument;
152 final Expression invocationTarget = invocation.getTarget();
153
154 if (!(invocationTarget instanceof MemberReferenceExpression)) {
155 return super.visitSwitchStatement(node, data);
156 }
157
158 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
159
160 if (!"ordinal".equals(memberReference.getMemberName())) {
161 return super.visitSwitchStatement(node, data);
162 }
163
164 final String enclosingTypeName = enclosingType.getInternalName();
165
166 SwitchMapInfo info = _switchMaps.get(enclosingTypeName);
167
168 if (info == null) {
169 _switchMaps.put(enclosingTypeName, info = new SwitchMapInfo(enclosingTypeName));
170
171 final TypeDefinition resolvedType = enclosingType.resolve();
172
173 if (resolvedType != null) {
174 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
175
176 if (astBuilder == null) {
177 astBuilder = new AstBuilder(context);
178 }
179
180 try (final SafeCloseable importSuppression = astBuilder.suppressImports()) {
181 final TypeDeclaration declaration = astBuilder.createType(resolvedType);
182
183 declaration.acceptVisitor(this, data);
184 }
185 }
186 }
187
188 List<SwitchStatement> switches = info.switches.get(mapName);
189
190 if (switches == null) {
191 info.switches.put(mapName, switches = new ArrayList<>());
192 }
193
194 switches.add(node);
195 }
196
197 return super.visitSwitchStatement(node, data);
198 }
199
200 @Override
201 public Void visitAssignmentExpression(final AssignmentExpression node, final Void data) {
202 final TypeDefinition currentType = context.getCurrentType();
203 final MethodDefinition currentMethod = context.getCurrentMethod();
204
205 if (_isSwitchMapWrapper &&
206 currentType != null &&
207 currentMethod != null &&
208 currentMethod.isTypeInitializer()) {
209
210 final Expression left = node.getLeft();
211 final Expression right = node.getRight();
212
213 if (left instanceof IndexerExpression &&
214 right instanceof PrimitiveExpression) {
215
216 String mapName = null;
217
218 final Expression array = ((IndexerExpression) left).getTarget();
219 final Expression argument = ((IndexerExpression) left).getArgument();
220
221 if (array instanceof MemberReferenceExpression) {
222 mapName = ((MemberReferenceExpression) array).getMemberName();
223 }
224 else if (array instanceof IdentifierExpression) {
225 mapName = ((IdentifierExpression) array).getIdentifier();
226 }
227
228 if (mapName == null || mapName.startsWith("$SwitchMap$")) {
229 return super.visitAssignmentExpression(node, data);
230 }
231
232 if (!(argument instanceof InvocationExpression)) {
233 return super.visitAssignmentExpression(node, data);
234 }
235
236 final InvocationExpression invocation = (InvocationExpression) argument;
237 final Expression invocationTarget = invocation.getTarget();
238
239 if (!(invocationTarget instanceof MemberReferenceExpression)) {
240 return super.visitAssignmentExpression(node, data);
241 }
242
243 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
244 final Expression memberTarget = memberReference.getTarget();
245
246 if (!(memberTarget instanceof MemberReferenceExpression) || !"ordinal".equals(memberReference.getMemberName())) {
247 return super.visitAssignmentExpression(node, data);
248 }
249
250 final MemberReferenceExpression outerMemberReference = (MemberReferenceExpression) memberTarget;
251 final Expression outerMemberTarget = outerMemberReference.getTarget();
252
253 if (!(outerMemberTarget instanceof TypeReferenceExpression)) {
254 return super.visitAssignmentExpression(node, data);
255 }
256
257 final String enclosingType = currentType.getInternalName();
258
259 SwitchMapInfo info = _switchMaps.get(enclosingType);
260
261 if (info == null) {
262 _switchMaps.put(enclosingType, info = new SwitchMapInfo(enclosingType));
263
264 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
265
266 if (astBuilder == null) {
267 astBuilder = new AstBuilder(context);
268 }
269
270 info.enclosingTypeDeclaration = astBuilder.createType(currentType);
271 }
272
273 final PrimitiveExpression value = (PrimitiveExpression) right;
274
275 assert value.getValue() instanceof Integer;
276
277 Map<Integer, Expression> mapping = info.mappings.get(mapName);
278
279 if (mapping == null) {
280 info.mappings.put(mapName, mapping = new LinkedHashMap<>());
281 }
282
283 final IdentifierExpression enumValue = new IdentifierExpression( Expression.MYSTERY_OFFSET, outerMemberReference.getMemberName());
284
285 enumValue.putUserData(Keys.MEMBER_REFERENCE, outerMemberReference.getUserData(Keys.MEMBER_REFERENCE));
286
287 mapping.put(((Number) value.getValue()).intValue(), enumValue);
288 }
289 }
290
291 return super.visitAssignmentExpression(node, data);
292 }
293
294 private void rewrite() {
295 if (_switchMaps.isEmpty()) {
296 return;
297 }
298
299 for (final SwitchMapInfo info : _switchMaps.values()) {
300 rewrite(info);
301 }
302
303 //
304 // Remove switch map type wrappers that are no longer referenced.
305 //
306
307 outer:
308 for (final SwitchMapInfo info : _switchMaps.values()) {
309 for (final String mapName : info.switches.keySet()) {
310 final List<SwitchStatement> switches = info.switches.get(mapName);
311
312 if (switches != null && !switches.isEmpty()) {
313 continue outer;
314 }
315 }
316
317 final TypeDeclaration enclosingTypeDeclaration = info.enclosingTypeDeclaration;
318
319 if (enclosingTypeDeclaration != null) {
320 enclosingTypeDeclaration.remove();
321 }
322 }
323 }
324
325 private void rewrite(final SwitchMapInfo info) {
326 if (info.switches.isEmpty()) {
327 return;
328 }
329
330 for (final String mapName : info.switches.keySet()) {
331 final List<SwitchStatement> switches = info.switches.get(mapName);
332 final Map<Integer, Expression> mappings = info.mappings.get(mapName);
333
334 if (switches != null && mappings != null) {
335 for (int i = 0; i < switches.size(); i++) {
336 if (rewriteSwitch(switches.get(i), mappings)) {
337 switches.remove(i--);
338 }
339 }
340 }
341 }
342 }
343
344 private boolean rewriteSwitch(final SwitchStatement s, final Map<Integer, Expression> mappings) {
345 final Map<Expression, Expression> replacements = new IdentityHashMap<>();
346
347 for (final SwitchSection section : s.getSwitchSections()) {
348 for (final CaseLabel caseLabel : section.getCaseLabels()) {
349 final Expression expression = caseLabel.getExpression();
350
351 if (expression.isNull()) {
352 continue;
353 }
354
355 if (expression instanceof PrimitiveExpression) {
356 final Object value = ((PrimitiveExpression) expression).getValue();
357
358 if (value instanceof Integer) {
359 final Expression replacement = mappings.get(value);
360
361 if (replacement != null) {
362 replacements.put(expression, replacement);
363 continue;
364 }
365 }
366 }
367
368 //
369 // If we can't rewrite all cases, we abort.
370 //
371
372 return false;
373 }
374 }
375
376 final IndexerExpression indexer = (IndexerExpression) s.getExpression();
377 final InvocationExpression argument = (InvocationExpression) indexer.getArgument();
378 final MemberReferenceExpression memberReference = (MemberReferenceExpression) argument.getTarget();
379 final Expression newTest = memberReference.getTarget();
380
381 newTest.remove();
382 indexer.replaceWith(newTest);
383
384 for (final Map.Entry<Expression, Expression> entry : replacements.entrySet()) {
385 entry.getKey().replaceWith(entry.getValue().clone());
386 }
387
388 return true;
389 }
390
391 private static boolean isSwitchMapWrapper(final TypeReference type) {
392 if (type == null) {
393 return false;
394 }
395
396 final TypeDefinition definition = type instanceof TypeDefinition ? (TypeDefinition) type
397 : type.resolve();
398
399 if (definition == null || !definition.isSynthetic() || !definition.isInnerClass() || !definition.isPackagePrivate()) {
400 return false;
401 }
402
403 for (final FieldDefinition field : definition.getDeclaredFields()) {
404 if (!field.getName().startsWith("$SwitchMap$") &&
405 BuiltinTypes.Integer.makeArrayType().equals(field.getFieldType())) {
406
407 return true;
408 }
409 }
410
411 return false;
412 }
413 }
414} \ No newline at end of file
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java
deleted file mode 100644
index cf0376f..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java
+++ /dev/null
@@ -1,39 +0,0 @@
1package cuchaz.enigma.source.procyon.transformers;
2
3import com.strobel.assembler.metadata.BuiltinTypes;
4import com.strobel.decompiler.DecompilerContext;
5import com.strobel.decompiler.languages.java.ast.AstNode;
6import com.strobel.decompiler.languages.java.ast.CastExpression;
7import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor;
8import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
9
10/**
11 * Created by Thiakil on 11/07/2018.
12 */
13public class RemoveObjectCasts implements IAstTransform {
14 private final DecompilerContext _context;
15
16 public RemoveObjectCasts(DecompilerContext context) {
17 _context = context;
18 }
19
20 @Override
21 public void run(AstNode compilationUnit) {
22 compilationUnit.acceptVisitor(new Visitor(_context), null);
23 }
24
25 private final static class Visitor extends ContextTrackingVisitor<Void>{
26
27 protected Visitor(DecompilerContext context) {
28 super(context);
29 }
30
31 @Override
32 public Void visitCastExpression(CastExpression node, Void data) {
33 if (node.getType().toTypeReference().equals(BuiltinTypes.Object)){
34 node.replaceWith(node.getExpression());
35 }
36 return super.visitCastExpression(node, data);
37 }
38 }
39}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java
deleted file mode 100644
index d3ddaab..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java
+++ /dev/null
@@ -1,197 +0,0 @@
1package cuchaz.enigma.source.procyon.transformers;
2
3import com.strobel.assembler.metadata.MemberReference;
4import com.strobel.assembler.metadata.MetadataFilters;
5import com.strobel.assembler.metadata.MetadataHelper;
6import com.strobel.assembler.metadata.MethodBinder;
7import com.strobel.assembler.metadata.MethodDefinition;
8import com.strobel.assembler.metadata.MethodReference;
9import com.strobel.assembler.metadata.TypeReference;
10import com.strobel.core.StringUtilities;
11import com.strobel.core.VerifyArgument;
12import com.strobel.decompiler.DecompilerContext;
13import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression;
14import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression;
15import com.strobel.decompiler.languages.java.ast.AstNode;
16import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
17import com.strobel.decompiler.languages.java.ast.CastExpression;
18import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor;
19import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
20import com.strobel.decompiler.languages.java.ast.Expression;
21import com.strobel.decompiler.languages.java.ast.InvocationExpression;
22import com.strobel.decompiler.languages.java.ast.JavaResolver;
23import com.strobel.decompiler.languages.java.ast.Keys;
24import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
25import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
26import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
27import com.strobel.decompiler.semantics.ResolveResult;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Created by Thiakil on 12/07/2018.
34 */
35public class VarargsFixer implements IAstTransform {
36 private final DecompilerContext _context;
37
38 public VarargsFixer(final DecompilerContext context) {
39 _context = VerifyArgument.notNull(context, "context");
40 }
41
42 @Override
43 public void run(AstNode compilationUnit) {
44 compilationUnit.acceptVisitor(new Visitor(_context), null);
45 }
46
47 class Visitor extends ContextTrackingVisitor<Void> {
48 private final JavaResolver _resolver;
49 protected Visitor(DecompilerContext context) {
50 super(context);
51 _resolver = new JavaResolver(context);
52 }
53
54 //remove `new Object[0]` on varagrs as the normal tranformer doesnt do them
55 @Override
56 public Void visitInvocationExpression(InvocationExpression node, Void data) {
57 super.visitInvocationExpression(node, data);
58 MemberReference definition = node.getUserData(Keys.MEMBER_REFERENCE);
59 if (definition instanceof MethodDefinition && ((MethodDefinition) definition).isVarArgs()){
60 AstNodeCollection<Expression> arguments = node.getArguments();
61 Expression lastParam = arguments.lastOrNullObject();
62 if (!lastParam.isNull() && lastParam instanceof ArrayCreationExpression){
63 ArrayCreationExpression varargArray = (ArrayCreationExpression)lastParam;
64 if (varargArray.getInitializer().isNull() || varargArray.getInitializer().getElements().isEmpty()){
65 lastParam.remove();
66 } else {
67 for (Expression e : varargArray.getInitializer().getElements()){
68 arguments.insertBefore(varargArray, e.clone());
69 }
70 varargArray.remove();
71 }
72 }
73 }
74 return null;
75 }
76
77 //applies the vararg transform to object creation
78 @Override
79 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) {
80 super.visitObjectCreationExpression(node, data);
81 final AstNodeCollection<Expression> arguments = node.getArguments();
82 final Expression lastArgument = arguments.lastOrNullObject();
83
84 Expression arrayArg = lastArgument;
85
86 if (arrayArg instanceof CastExpression)
87 arrayArg = ((CastExpression) arrayArg).getExpression();
88
89 if (arrayArg == null ||
90 arrayArg.isNull() ||
91 !(arrayArg instanceof ArrayCreationExpression &&
92 node.getTarget() instanceof MemberReferenceExpression)) {
93
94 return null;
95 }
96
97 final ArrayCreationExpression newArray = (ArrayCreationExpression) arrayArg;
98 final MemberReferenceExpression target = (MemberReferenceExpression) node.getTarget();
99
100 if (!newArray.getAdditionalArraySpecifiers().hasSingleElement()) {
101 return null;
102 }
103
104 final MethodReference method = (MethodReference) node.getUserData(Keys.MEMBER_REFERENCE);
105
106 if (method == null) {
107 return null;
108 }
109
110 final MethodDefinition resolved = method.resolve();
111
112 if (resolved == null || !resolved.isVarArgs()) {
113 return null;
114 }
115
116 final List<MethodReference> candidates;
117 final Expression invocationTarget = target.getTarget();
118
119 if (invocationTarget == null || invocationTarget.isNull()) {
120 candidates = MetadataHelper.findMethods(
121 context.getCurrentType(),
122 MetadataFilters.matchName(resolved.getName())
123 );
124 }
125 else {
126 final ResolveResult targetResult = _resolver.apply(invocationTarget);
127
128 if (targetResult == null || targetResult.getType() == null) {
129 return null;
130 }
131
132 candidates = MetadataHelper.findMethods(
133 targetResult.getType(),
134 MetadataFilters.matchName(resolved.getName())
135 );
136 }
137
138 final List<TypeReference> argTypes = new ArrayList<>();
139
140 for (final Expression argument : arguments) {
141 final ResolveResult argResult = _resolver.apply(argument);
142
143 if (argResult == null || argResult.getType() == null) {
144 return null;
145 }
146
147 argTypes.add(argResult.getType());
148 }
149
150 final MethodBinder.BindResult c1 = MethodBinder.selectMethod(candidates, argTypes);
151
152 if (c1.isFailure() || c1.isAmbiguous()) {
153 return null;
154 }
155
156 argTypes.remove(argTypes.size() - 1);
157
158 final ArrayInitializerExpression initializer = newArray.getInitializer();
159 final boolean hasElements = !initializer.isNull() && !initializer.getElements().isEmpty();
160
161 if (hasElements) {
162 for (final Expression argument : initializer.getElements()) {
163 final ResolveResult argResult = _resolver.apply(argument);
164
165 if (argResult == null || argResult.getType() == null) {
166 return null;
167 }
168
169 argTypes.add(argResult.getType());
170 }
171 }
172
173 final MethodBinder.BindResult c2 = MethodBinder.selectMethod(candidates, argTypes);
174
175 if (c2.isFailure() ||
176 c2.isAmbiguous() ||
177 !StringUtilities.equals(c2.getMethod().getErasedSignature(), c1.getMethod().getErasedSignature())) {
178
179 return null;
180 }
181
182 lastArgument.remove();
183
184 if (!hasElements) {
185 lastArgument.remove();
186 return null;
187 }
188
189 for (final Expression newArg : initializer.getElements()) {
190 newArg.remove();
191 arguments.add(newArg);
192 }
193
194 return null;
195 }
196 }
197}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java
deleted file mode 100644
index e702956..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java
+++ /dev/null
@@ -1,33 +0,0 @@
1package cuchaz.enigma.source.procyon.typeloader;
2
3import com.strobel.assembler.metadata.Buffer;
4import com.strobel.assembler.metadata.ClasspathTypeLoader;
5import com.strobel.assembler.metadata.ITypeLoader;
6
7/**
8 * Caching version of {@link ClasspathTypeLoader}
9 */
10public class CachingClasspathTypeLoader extends CachingTypeLoader {
11 private static ITypeLoader extraClassPathLoader = null;
12
13 public static void setExtraClassPathLoader(ITypeLoader loader){
14 extraClassPathLoader = loader;
15 }
16
17 private final ITypeLoader classpathLoader = new ClasspathTypeLoader();
18
19 @Override
20 protected byte[] doLoad(String className) {
21 Buffer parentBuf = new Buffer();
22 if (classpathLoader.tryLoadType(className, parentBuf)) {
23 return parentBuf.array();
24 }
25 if (extraClassPathLoader != null){
26 parentBuf.reset();
27 if (extraClassPathLoader.tryLoadType(className, parentBuf)){
28 return parentBuf.array();
29 }
30 }
31 return EMPTY_ARRAY;//need to return *something* as null means no store
32 }
33}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java
deleted file mode 100644
index 5be5ddd..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java
+++ /dev/null
@@ -1,38 +0,0 @@
1package cuchaz.enigma.source.procyon.typeloader;
2
3import com.google.common.collect.Maps;
4import com.strobel.assembler.metadata.Buffer;
5import com.strobel.assembler.metadata.ITypeLoader;
6
7import java.util.Map;
8
9/**
10 * Common cache functions
11 */
12public abstract class CachingTypeLoader implements ITypeLoader {
13 protected static final byte[] EMPTY_ARRAY = {};
14
15 private final Map<String, byte[]> cache = Maps.newHashMap();
16
17 protected abstract byte[] doLoad(String className);
18
19 @Override
20 public boolean tryLoadType(String className, Buffer out) {
21
22 // check the cache
23 byte[] data = this.cache.computeIfAbsent(className, this::doLoad);
24
25 if (data == EMPTY_ARRAY) {
26 return false;
27 }
28
29 out.reset(data.length);
30 System.arraycopy(data, 0, out.array(), out.position(), data.length);
31 out.position(0);
32 return true;
33 }
34
35 public void clearCache() {
36 this.cache.clear();
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java
deleted file mode 100644
index e703d3b..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java
+++ /dev/null
@@ -1,140 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.source.procyon.typeloader;
13
14import com.google.common.collect.Lists;
15import com.strobel.assembler.metadata.Buffer;
16import com.strobel.assembler.metadata.ITypeLoader;
17import cuchaz.enigma.ClassProvider;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import org.objectweb.asm.ClassVisitor;
20import org.objectweb.asm.ClassWriter;
21import org.objectweb.asm.Opcodes;
22import org.objectweb.asm.tree.AbstractInsnNode;
23import org.objectweb.asm.tree.ClassNode;
24import org.objectweb.asm.tree.MethodInsnNode;
25import org.objectweb.asm.tree.MethodNode;
26
27import java.util.Collection;
28import java.util.LinkedList;
29import java.util.List;
30import java.util.function.Function;
31
32public class CompiledSourceTypeLoader extends CachingTypeLoader {
33 //Store one instance as the classpath shouldn't change during load
34 private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader();
35
36 private final ClassProvider compiledSource;
37 private final LinkedList<Function<ClassVisitor, ClassVisitor>> visitors = new LinkedList<>();
38
39 public CompiledSourceTypeLoader(ClassProvider compiledSource) {
40 this.compiledSource = compiledSource;
41 }
42
43 public void addVisitor(Function<ClassVisitor, ClassVisitor> visitor) {
44 this.visitors.addFirst(visitor);
45 }
46
47 @Override
48 protected byte[] doLoad(String className) {
49 byte[] data = loadType(className);
50 if (data == null) {
51 return loadClasspath(className);
52 }
53
54 return data;
55 }
56
57 private byte[] loadClasspath(String name) {
58 Buffer parentBuf = new Buffer();
59 if (CLASSPATH_TYPE_LOADER.tryLoadType(name, parentBuf)) {
60 return parentBuf.array();
61 }
62 return EMPTY_ARRAY;
63 }
64
65 private byte[] loadType(String className) {
66 ClassEntry entry = new ClassEntry(className);
67
68 // find the class in the jar
69 ClassNode node = findClassNode(entry);
70 if (node == null) {
71 // couldn't find it
72 return null;
73 }
74
75 removeRedundantClassCalls(node);
76
77 ClassWriter writer = new ClassWriter(0);
78
79 ClassVisitor visitor = writer;
80 for (Function<ClassVisitor, ClassVisitor> visitorFunction : this.visitors) {
81 visitor = visitorFunction.apply(visitor);
82 }
83
84 node.accept(visitor);
85
86 // we have a transformed class!
87 return writer.toByteArray();
88 }
89
90 private void removeRedundantClassCalls(ClassNode node) {
91 // remove <obj>.getClass() calls that are seemingly injected
92 // DUP
93 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
94 // POP
95 for (MethodNode methodNode : node.methods) {
96 AbstractInsnNode insnNode = methodNode.instructions.getFirst();
97 while (insnNode != null) {
98 if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
99 MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
100 if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) {
101 AbstractInsnNode previous = methodInsnNode.getPrevious();
102 AbstractInsnNode next = methodInsnNode.getNext();
103 if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) {
104 insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction
105 methodNode.instructions.remove(previous);
106 methodNode.instructions.remove(methodInsnNode);
107 methodNode.instructions.remove(next);
108 }
109 }
110 }
111 insnNode = insnNode.getNext();
112 }
113 }
114 }
115
116 private ClassNode findClassNode(ClassEntry entry) {
117 // try to find the class in the jar
118 for (String className : getClassNamesToTry(entry)) {
119 ClassNode node = compiledSource.getClassNode(className);
120 if (node != null) {
121 return node;
122 }
123 }
124
125 // didn't find it ;_;
126 return null;
127 }
128
129 private Collection<String> getClassNamesToTry(ClassEntry entry) {
130 List<String> classNamesToTry = Lists.newArrayList();
131 classNamesToTry.add(entry.getFullName());
132
133 ClassEntry outerClass = entry.getOuterClass();
134 if (outerClass != null) {
135 classNamesToTry.addAll(getClassNamesToTry(outerClass));
136 }
137
138 return classNamesToTry;
139 }
140}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java
deleted file mode 100644
index c4732b0..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java
+++ /dev/null
@@ -1,38 +0,0 @@
1package cuchaz.enigma.source.procyon.typeloader;
2
3import com.strobel.assembler.metadata.ITypeLoader;
4import com.strobel.assembler.metadata.MetadataSystem;
5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference;
7
8import java.util.Collections;
9import java.util.Set;
10import java.util.concurrent.ConcurrentHashMap;
11
12public final class NoRetryMetadataSystem extends MetadataSystem {
13 private final Set<String> failedTypes = Collections.newSetFromMap(new ConcurrentHashMap<>());
14
15 public NoRetryMetadataSystem(final ITypeLoader typeLoader) {
16 super(typeLoader);
17 }
18
19 @Override
20 protected synchronized TypeDefinition resolveType(final String descriptor, final boolean mightBePrimitive) {
21 if (failedTypes.contains(descriptor)) {
22 return null;
23 }
24
25 final TypeDefinition result = super.resolveType(descriptor, mightBePrimitive);
26
27 if (result == null) {
28 failedTypes.add(descriptor);
29 }
30
31 return result;
32 }
33
34 @Override
35 public synchronized TypeDefinition resolve(final TypeReference type) {
36 return super.resolve(type);
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java
deleted file mode 100644
index 86c6ecc..0000000
--- a/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java
+++ /dev/null
@@ -1,20 +0,0 @@
1package cuchaz.enigma.source.procyon.typeloader;
2
3import com.strobel.assembler.metadata.Buffer;
4import com.strobel.assembler.metadata.ITypeLoader;
5
6/**
7 * Typeloader with synchronized tryLoadType method
8 */
9public class SynchronizedTypeLoader implements ITypeLoader {
10 private final ITypeLoader delegate;
11
12 public SynchronizedTypeLoader(ITypeLoader delegate) {
13 this.delegate = delegate;
14 }
15
16 @Override
17 public synchronized boolean tryLoadType(String internalName, Buffer buffer) {
18 return delegate.tryLoadType(internalName, buffer);
19 }
20}