summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/source/cfr
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/source/cfr')
-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.java427
3 files changed, 573 insertions, 0 deletions
diff --git a/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
new file mode 100644
index 0000000..9e37f16
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
@@ -0,0 +1,108 @@
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
new file mode 100644
index 0000000..d4f2da6
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
@@ -0,0 +1,38 @@
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
new file mode 100644
index 0000000..b9cdbea
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
@@ -0,0 +1,427 @@
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 type = type.getDeGenerifiedType();
47
48 if (type instanceof JavaRefTypeInstance) {
49 return "L" + type.getRawName().replace('.', '/') + ";";
50 }
51
52 if (type instanceof JavaArrayTypeInstance) {
53 return "[" + getDesc(((JavaArrayTypeInstance) type).removeAnArrayIndirection());
54 }
55
56 if (type instanceof RawJavaType) {
57 switch ((RawJavaType) type) {
58 case BOOLEAN:
59 return "Z";
60 case BYTE:
61 return "B";
62 case CHAR:
63 return "C";
64 case SHORT:
65 return "S";
66 case INT:
67 return "I";
68 case LONG:
69 return "J";
70 case FLOAT:
71 return "F";
72 case DOUBLE:
73 return "D";
74 case VOID:
75 return "V";
76 default:
77 throw new AssertionError();
78 }
79 }
80
81 throw new AssertionError();
82 }
83
84 private MethodEntry getMethodEntry(MethodPrototype method) {
85 if (method.getClassType() == null) {
86 return null;
87 }
88
89 MethodDescriptor desc = new MethodDescriptor(
90 method.getArgs().stream().map(type -> new TypeDescriptor(getDesc(type))).collect(Collectors.toList()),
91 new TypeDescriptor(method.getName().equals("<init>") || method.getName().equals("<clinit>") ? "V" : getDesc(method.getReturnType()))
92 );
93
94 return new MethodEntry(getClassEntry(method.getClassType()), method.getName(), desc);
95 }
96
97 private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) {
98 int variableIndex = method.isInstanceMethod() ? 1 : 0;
99 for (int i = 0; i < parameterIndex; i++) {
100 variableIndex += method.getArgs().get(parameterIndex).getStackType().getComputationCategory();
101 }
102
103 return new LocalVariableEntry(getMethodEntry(method), variableIndex, name, true, null);
104 }
105
106 private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, JavaTypeInstance type) {
107 return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(getDesc(type)));
108 }
109
110 private ClassEntry getClassEntry(JavaTypeInstance type) {
111 return new ClassEntry(type.getRawName().replace('.', '/'));
112 }
113
114 @Override
115 public Dumper beginBlockComment(boolean inline) {
116 print("/*").newln();
117 return this;
118 }
119
120 @Override
121 public Dumper endBlockComment() {
122 print(" */").newln();
123 return this;
124 }
125
126 @Override
127 public Dumper label(String s, boolean inline) {
128 processPendingCR();
129 append(s);
130 append(":");
131 return this;
132 }
133
134 @Override
135 public Dumper comment(String s) {
136 append("// ");
137 append(s);
138 append("\n");
139 return this;
140 }
141
142 @Override
143 public void enqueuePendingCarriageReturn() {
144 pendingCR = true;
145 }
146
147 @Override
148 public Dumper removePendingCarriageReturn() {
149 pendingCR = false;
150 return this;
151 }
152
153 private void processPendingCR() {
154 if (pendingCR) {
155 append("\n");
156 atStart = true;
157 pendingCR = false;
158 }
159 }
160
161 @Override
162 public Dumper identifier(String s, Object ref, boolean defines) {
163 return print(s);
164 }
165
166 @Override
167 public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) {
168 doIndent();
169 Token token = new Token(position, position + name.length(), name);
170 Entry<?> entry = getMethodEntry(method);
171
172 if (entry != null) {
173 if (defines) {
174 index.addDeclaration(token, entry);
175 } else {
176 index.addReference(token, entry, null);
177 }
178 }
179
180 return identifier(name, null, defines);
181 }
182
183 @Override
184 public Dumper parameterName(String name, MethodPrototype method, int index, boolean defines) {
185 doIndent();
186 Token token = new Token(position, position + name.length(), name);
187 Entry<?> entry = getParameterEntry(method, index, name);
188
189 if (entry != null) {
190 if (defines) {
191 this.index.addDeclaration(token, entry);
192 } else {
193 this.index.addReference(token, entry, null);
194 }
195 }
196
197 return identifier(name, null, defines);
198 }
199
200 @Override
201 public Dumper variableName(String name, NamedVariable variable, boolean defines) {
202 return identifier(name, null, defines);
203 }
204
205 @Override
206 public Dumper packageName(JavaRefTypeInstance t) {
207 String s = t.getPackageName();
208
209 if (!s.isEmpty()) {
210 keyword("package ").print(s).endCodeln().newln();
211 }
212
213 return this;
214 }
215
216 @Override
217 public Dumper fieldName(String name, Field field, JavaTypeInstance owner, boolean hiddenDeclaration, boolean defines) {
218 doIndent();
219 Token token = new Token(position, position + name.length(), name);
220 Entry<?> entry = field == null ? null : getFieldEntry(owner, name, field.getJavaTypeInstance());
221
222 if (entry != null) {
223 if (defines) {
224 index.addDeclaration(token, entry);
225 } else {
226 index.addReference(token, entry, null);
227 }
228 }
229
230 identifier(name, null, defines);
231 return this;
232 }
233
234 @Override
235 public Dumper print(String s) {
236 processPendingCR();
237 doIndent();
238 append(s);
239 atStart = s.endsWith("\n");
240 outputCount++;
241 return this;
242 }
243
244 @Override
245 public Dumper print(char c) {
246 return print(String.valueOf(c));
247 }
248
249 @Override
250 public Dumper newln() {
251 append("\n");
252 atStart = true;
253 outputCount++;
254 return this;
255 }
256
257 @Override
258 public Dumper endCodeln() {
259 append(";\n");
260 atStart = true;
261 outputCount++;
262 return this;
263 }
264
265 @Override
266 public Dumper keyword(String s) {
267 print(s);
268 return this;
269 }
270
271 @Override
272 public Dumper operator(String s) {
273 print(s);
274 return this;
275 }
276
277 @Override
278 public Dumper separator(String s) {
279 print(s);
280 return this;
281 }
282
283 @Override
284 public Dumper literal(String s, Object o) {
285 print(s);
286 return this;
287 }
288
289 private void doIndent() {
290 if (!atStart) return;
291 String indents = " ";
292
293 for (int x = 0; x < indent; ++x) {
294 append(indents);
295 }
296
297 atStart = false;
298 }
299
300 @Override
301 public void indent(int diff) {
302 indent += diff;
303 }
304
305 @Override
306 public Dumper dump(Dumpable d) {
307 if (d == null) {
308 keyword("null");
309 return this;
310 }
311
312 d.dump(this);
313 return this;
314 }
315
316 @Override
317 public TypeUsageInformation getTypeUsageInformation() {
318 return typeUsageInformation;
319 }
320
321 @Override
322 public ObfuscationMapping getObfuscationMapping() {
323 return NullMapping.INSTANCE;
324 }
325
326 @Override
327 public String toString() {
328 return sb.toString();
329 }
330
331 @Override
332 public void addSummaryError(Method method, String s) {}
333
334 @Override
335 public void close() {
336 }
337
338 @Override
339 public boolean canEmitClass(JavaTypeInstance type) {
340 return emitted.add(type);
341 }
342
343 @Override
344 public int getOutputCount() {
345 return outputCount;
346 }
347
348 @Override
349 public Dumper dump(JavaTypeInstance type) {
350 return dump(type, TypeContext.None, false);
351 }
352
353 @Override
354 public Dumper dump(JavaTypeInstance type, boolean defines) {
355 return dump(type, TypeContext.None, false);
356 }
357
358 @Override
359 public Dumper dump(JavaTypeInstance type, TypeContext context) {
360 return dump(type, context, false);
361 }
362
363 private Dumper dump(JavaTypeInstance type, TypeContext context, boolean defines) {
364 doIndent();
365 if (type instanceof JavaRefTypeInstance) {
366 int start = position;
367 type.dumpInto(this, typeUsageInformation, TypeContext.None);
368 int end = position;
369 Token token = new Token(start, end, sb.toString().substring(start, end));
370
371 if (defines) {
372 index.addDeclaration(token, getClassEntry(type));
373 } else {
374 index.addReference(token, getClassEntry(type), null);
375 }
376
377 return this;
378 }
379
380 type.dumpInto(this, typeUsageInformation, context);
381 return this;
382 }
383
384 @Override
385 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
386 return new WithTypeUsageInformationDumper(this, innerclassTypeUsageInformation);
387 }
388
389 public SourceIndex getIndex() {
390 index.setSource(getString());
391 return index;
392 }
393
394 public String getString() {
395 return sb.toString();
396 }
397
398 public static class WithTypeUsageInformationDumper extends DelegatingDumper {
399 private final TypeUsageInformation typeUsageInformation;
400
401 WithTypeUsageInformationDumper(Dumper delegate, TypeUsageInformation typeUsageInformation) {
402 super(delegate);
403 this.typeUsageInformation = typeUsageInformation;
404 }
405
406 @Override
407 public TypeUsageInformation getTypeUsageInformation() {
408 return typeUsageInformation;
409 }
410
411 @Override
412 public Dumper dump(JavaTypeInstance javaTypeInstance) {
413 return dump(javaTypeInstance, TypeContext.None);
414 }
415
416 @Override
417 public Dumper dump(JavaTypeInstance javaTypeInstance, TypeContext typeContext) {
418 javaTypeInstance.dumpInto(this, typeUsageInformation, typeContext);
419 return this;
420 }
421
422 @Override
423 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
424 return new WithTypeUsageInformationDumper(delegate, innerclassTypeUsageInformation);
425 }
426 }
427}