summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar liach2021-01-21 19:09:56 -0600
committerGravatar liach2021-01-23 10:16:16 -0600
commitabcd9a8fdabc2e5a101b772b534ca900d469374b (patch)
tree54326a50291135f64b95d78bb3450228719eb642
parentAttempt fixing rare CME when loading a jar while tabs are open (diff)
downloadenigma-abcd9a8fdabc2e5a101b772b534ca900d469374b.tar.gz
enigma-abcd9a8fdabc2e5a101b772b534ca900d469374b.tar.xz
enigma-abcd9a8fdabc2e5a101b772b534ca900d469374b.zip
Make cfr much easier to use
Now it can display javadoc, remove imports, highlight local variables Depends on FabricMC/cfr#2 Signed-off-by: liach <liach@users.noreply.github.com> More cfr cleanup. Cfr looks good now Remove outdated note Allows requesting javadoc on initial source creation. Currently both cfr and procyon would be more efficient this way. Signed-off-by: liach <liach@users.noreply.github.com> Now renders inner classes properly (and with updated cfr now renders lambda params) Signed-off-by: liach <liach@users.noreply.github.com> Tweaks to handle purely generic fields and cfr problem classes like ClientEntityManager.Listener Signed-off-by: liach <liach@users.noreply.github.com>
-rw-r--r--enigma/build.gradle8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/EnigmaProject.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/Decompiler.java10
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/Source.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java13
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java19
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java494
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java2
10 files changed, 255 insertions, 311 deletions
diff --git a/enigma/build.gradle b/enigma/build.gradle
index 19f8d160..be449e27 100644
--- a/enigma/build.gradle
+++ b/enigma/build.gradle
@@ -2,6 +2,9 @@ configurations {
2 proGuard 2 proGuard
3} 3}
4 4
5repositories.mavenLocal()
6repositories.jcenter()
7
5dependencies { 8dependencies {
6 implementation 'org.ow2.asm:asm:9.0' 9 implementation 'org.ow2.asm:asm:9.0'
7 implementation 'org.ow2.asm:asm-commons:9.0' 10 implementation 'org.ow2.asm:asm-commons:9.0'
@@ -9,11 +12,12 @@ dependencies {
9 implementation 'org.ow2.asm:asm-util:9.0' 12 implementation 'org.ow2.asm:asm-util:9.0'
10 13
11 implementation 'net.fabricmc:procyon-fabric-compilertools:0.5.35.13' 14 implementation 'net.fabricmc:procyon-fabric-compilertools:0.5.35.13'
12 implementation 'net.fabricmc:cfr:0.0.1' 15 implementation 'net.fabricmc:cfr:local'
13 16
14 testImplementation 'junit:junit:4.+' 17 testImplementation 'junit:junit:4.+'
15 testImplementation 'org.hamcrest:hamcrest-all:1.+' 18 testImplementation 'org.hamcrest:hamcrest-all:1.+'
16 proGuard 'net.sf.proguard:proguard-base:6.+' 19 proGuard 'com.guardsquare:proguard-base:7.0.1'
20 // proguard does not yet support java 15. Add -Dorg.gradle.java.home="<olderjdk>" to bypass that
17} 21}
18 22
19// Generate "version.txt" file 23// Generate "version.txt" file
diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java
index a01eca19..a9e022fa 100644
--- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java
+++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java
@@ -215,13 +215,15 @@ public class EnigmaProject {
215 .filter(Objects::nonNull) 215 .filter(Objects::nonNull)
216 .collect(Collectors.toMap(n -> n.name, Functions.identity())); 216 .collect(Collectors.toMap(n -> n.name, Functions.identity()));
217 217
218 return new JarExport(compiled); 218 return new JarExport(mapper, compiled);
219 } 219 }
220 220
221 public static final class JarExport { 221 public static final class JarExport {
222 private final EntryRemapper mapper;
222 private final Map<String, ClassNode> compiled; 223 private final Map<String, ClassNode> compiled;
223 224
224 JarExport(Map<String, ClassNode> compiled) { 225 JarExport(EntryRemapper mapper, Map<String, ClassNode> compiled) {
226 this.mapper = mapper;
225 this.compiled = compiled; 227 this.compiled = compiled;
226 } 228 }
227 229
@@ -293,7 +295,7 @@ public class EnigmaProject {
293 } 295 }
294 296
295 private String decompileClass(ClassNode translatedNode, Decompiler decompiler) { 297 private String decompileClass(ClassNode translatedNode, Decompiler decompiler) {
296 return decompiler.getSource(translatedNode.name).asString(); 298 return decompiler.getSource(translatedNode.name, mapper).asString();
297 } 299 }
298 } 300 }
299 301
diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
index c9e3390d..71ef0b29 100644
--- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
@@ -255,7 +255,7 @@ public final class ClassHandleProvider {
255 int v = javadocVersion.incrementAndGet(); 255 int v = javadocVersion.incrementAndGet();
256 return f.thenApplyAsync(res -> { 256 return f.thenApplyAsync(res -> {
257 if (res == null || javadocVersion.get() != v) return null; 257 if (res == null || javadocVersion.get() != v) return null;
258 Result<Source, ClassHandleError> jdSource = res.map(s -> s.addJavadocs(p.project.getMapper())); 258 Result<Source, ClassHandleError> jdSource = res.map(s -> s.withJavadocs(p.project.getMapper()));
259 withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource)); 259 withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource));
260 return jdSource; 260 return jdSource;
261 }, p.pool); 261 }, p.pool);
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java b/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java
index c9666d52..938a7362 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java
@@ -1,5 +1,13 @@
1package cuchaz.enigma.source; 1package cuchaz.enigma.source;
2 2
3import cuchaz.enigma.translation.mapping.EntryRemapper;
4import org.checkerframework.checker.nullness.qual.Nullable;
5
3public interface Decompiler { 6public interface Decompiler {
4 Source getSource(String className); 7 @Deprecated // use remapper specific one for easy doc inclusion
8 default Source getSource(String className) {
9 return getSource(className, null);
10 }
11
12 Source getSource(String className, @Nullable EntryRemapper remapper);
5} 13}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Source.java b/enigma/src/main/java/cuchaz/enigma/source/Source.java
index 43c4de0c..6ecce7c5 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/Source.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/Source.java
@@ -5,7 +5,7 @@ import cuchaz.enigma.translation.mapping.EntryRemapper;
5public interface Source { 5public interface Source {
6 String asString(); 6 String asString();
7 7
8 Source addJavadocs(EntryRemapper remapper); 8 Source withJavadocs(EntryRemapper remapper);
9 9
10 SourceIndex index(); 10 SourceIndex index();
11} 11}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
index 941a699f..ffc4788c 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
@@ -4,6 +4,7 @@ import cuchaz.enigma.classprovider.ClassProvider;
4import cuchaz.enigma.source.Decompiler; 4import cuchaz.enigma.source.Decompiler;
5import cuchaz.enigma.source.Source; 5import cuchaz.enigma.source.Source;
6import cuchaz.enigma.source.SourceSettings; 6import cuchaz.enigma.source.SourceSettings;
7import cuchaz.enigma.translation.mapping.EntryRemapper;
7import cuchaz.enigma.utils.AsmUtil; 8import cuchaz.enigma.utils.AsmUtil;
8import org.benf.cfr.reader.apiunreleased.ClassFileSource2; 9import org.benf.cfr.reader.apiunreleased.ClassFileSource2;
9import org.benf.cfr.reader.apiunreleased.JarContent; 10import org.benf.cfr.reader.apiunreleased.JarContent;
@@ -19,6 +20,7 @@ import org.benf.cfr.reader.util.CannotLoadClassException;
19import org.benf.cfr.reader.util.collections.ListFactory; 20import org.benf.cfr.reader.util.collections.ListFactory;
20import org.benf.cfr.reader.util.getopt.Options; 21import org.benf.cfr.reader.util.getopt.Options;
21import org.benf.cfr.reader.util.getopt.OptionsImpl; 22import org.benf.cfr.reader.util.getopt.OptionsImpl;
23import org.checkerframework.checker.nullness.qual.Nullable;
22import org.objectweb.asm.tree.ClassNode; 24import org.objectweb.asm.tree.ClassNode;
23 25
24import java.util.Collection; 26import java.util.Collection;
@@ -27,6 +29,8 @@ import java.util.Map;
27 29
28public class CfrDecompiler implements Decompiler { 30public class CfrDecompiler implements Decompiler {
29 private final DCCommonState state; 31 private final DCCommonState state;
32 // cfr doesn't add final on params so final setting is ignored
33 private final SourceSettings settings;
30 34
31 public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { 35 public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) {
32 Map<String, String> options = new HashMap<>(); 36 Map<String, String> options = new HashMap<>();
@@ -63,10 +67,12 @@ public class CfrDecompiler implements Decompiler {
63 return new Pair<>(AsmUtil.nodeToBytes(node), path); 67 return new Pair<>(AsmUtil.nodeToBytes(node), path);
64 } 68 }
65 }); 69 });
70
71 this.settings = sourceSettings;
66 } 72 }
67 73
68 @Override 74 @Override
69 public Source getSource(String className) { 75 public Source getSource(String className, @Nullable EntryRemapper mapper) {
70 DCCommonState state = this.state; 76 DCCommonState state = this.state;
71 Options options = state.getOptions(); 77 Options options = state.getOptions();
72 78
@@ -79,7 +85,8 @@ public class CfrDecompiler implements Decompiler {
79 // To make sure we're analysing the cached version 85 // To make sure we're analysing the cached version
80 try { 86 try {
81 tree = state.getClassFile(tree.getClassType()); 87 tree = state.getClassFile(tree.getClassType());
82 } catch (CannotLoadClassException ignored) {} 88 } catch (CannotLoadClassException ignored) {
89 }
83 90
84 if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { 91 if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) {
85 tree.loadInnerClasses(state); 92 tree.loadInnerClasses(state);
@@ -91,6 +98,6 @@ public class CfrDecompiler implements Decompiler {
91 98
92 TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); 99 TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree);
93 tree.analyseTop(state, typeUsageCollector); 100 tree.analyseTop(state, typeUsageCollector);
94 return new CfrSource(tree, state, typeUsageCollector.getRealTypeUsageInformation()); 101 return new CfrSource(settings, tree, state, typeUsageCollector.getRealTypeUsageInformation(), options, mapper);
95 } 102 }
96} 103}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
index d4f2da6a..ba831d76 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
@@ -2,20 +2,31 @@ package cuchaz.enigma.source.cfr;
2 2
3import cuchaz.enigma.source.Source; 3import cuchaz.enigma.source.Source;
4import cuchaz.enigma.source.SourceIndex; 4import cuchaz.enigma.source.SourceIndex;
5import cuchaz.enigma.source.SourceSettings;
5import cuchaz.enigma.translation.mapping.EntryRemapper; 6import cuchaz.enigma.translation.mapping.EntryRemapper;
6import org.benf.cfr.reader.entities.ClassFile; 7import org.benf.cfr.reader.entities.ClassFile;
7import org.benf.cfr.reader.state.DCCommonState; 8import org.benf.cfr.reader.state.DCCommonState;
8import org.benf.cfr.reader.state.TypeUsageInformation; 9import org.benf.cfr.reader.state.TypeUsageInformation;
10import org.benf.cfr.reader.util.getopt.Options;
11import org.checkerframework.checker.nullness.qual.Nullable;
9 12
10public class CfrSource implements Source { 13public class CfrSource implements Source {
14 private final SourceSettings settings;
11 private final ClassFile tree; 15 private final ClassFile tree;
12 private final SourceIndex index; 16 private final SourceIndex index;
13 private final String string; 17 private final String string;
18 private final DCCommonState state;
19 private final TypeUsageInformation typeUsage;
20 private final Options options;
14 21
15 public CfrSource(ClassFile tree, DCCommonState state, TypeUsageInformation typeUsages) { 22 public CfrSource(SourceSettings settings, ClassFile tree, DCCommonState state, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) {
23 this.settings = settings;
16 this.tree = tree; 24 this.tree = tree;
25 this.state = state;
26 this.typeUsage = typeUsage;
27 this.options = options;
17 28
18 EnigmaDumper dumper = new EnigmaDumper(typeUsages); 29 EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsage, options, mapper);
19 tree.dump(state.getObfuscationMapping().wrap(dumper)); 30 tree.dump(state.getObfuscationMapping().wrap(dumper));
20 index = dumper.getIndex(); 31 index = dumper.getIndex();
21 string = dumper.getString(); 32 string = dumper.getString();
@@ -27,8 +38,8 @@ public class CfrSource implements Source {
27 } 38 }
28 39
29 @Override 40 @Override
30 public Source addJavadocs(EntryRemapper remapper) { 41 public Source withJavadocs(EntryRemapper mapper) {
31 return this; // TODO 42 return new CfrSource(settings, tree, state, typeUsage, options, mapper);
32 } 43 }
33 44
34 @Override 45 @Override
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
index e05a6ffa..2c6ec15a 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
@@ -1,90 +1,66 @@
1package cuchaz.enigma.source.cfr; 1package cuchaz.enigma.source.cfr;
2 2
3import cuchaz.enigma.source.Token;
4import cuchaz.enigma.source.SourceIndex; 3import cuchaz.enigma.source.SourceIndex;
4import cuchaz.enigma.source.SourceSettings;
5import cuchaz.enigma.source.Token;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.EntryRemapper;
5import cuchaz.enigma.translation.representation.MethodDescriptor; 8import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.TypeDescriptor; 9import cuchaz.enigma.translation.representation.TypeDescriptor;
7import cuchaz.enigma.translation.representation.entry.*; 10import cuchaz.enigma.translation.representation.entry.ClassEntry;
8import org.benf.cfr.reader.bytecode.analysis.types.*; 11import cuchaz.enigma.translation.representation.entry.Entry;
12import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
14import cuchaz.enigma.translation.representation.entry.MethodEntry;
15import org.benf.cfr.reader.bytecode.analysis.types.JavaArrayTypeInstance;
16import org.benf.cfr.reader.bytecode.analysis.types.JavaGenericBaseInstance;
17import org.benf.cfr.reader.bytecode.analysis.types.JavaRefTypeInstance;
18import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance;
19import org.benf.cfr.reader.bytecode.analysis.types.MethodPrototype;
20import org.benf.cfr.reader.bytecode.analysis.types.RawJavaType;
9import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable; 21import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable;
10import org.benf.cfr.reader.entities.Field; 22import 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; 23import org.benf.cfr.reader.state.TypeUsageInformation;
15import org.benf.cfr.reader.util.collections.SetFactory; 24import org.benf.cfr.reader.util.getopt.Options;
16import org.benf.cfr.reader.util.output.DelegatingDumper;
17import org.benf.cfr.reader.util.output.Dumpable;
18import org.benf.cfr.reader.util.output.Dumper; 25import org.benf.cfr.reader.util.output.Dumper;
26import org.benf.cfr.reader.util.output.IllegalIdentifierDump;
27import org.benf.cfr.reader.util.output.MovableDumperContext;
28import org.benf.cfr.reader.util.output.StringStreamDumper;
19import org.benf.cfr.reader.util.output.TypeContext; 29import org.benf.cfr.reader.util.output.TypeContext;
20 30import org.checkerframework.checker.nullness.qual.Nullable;
21import java.util.Set; 31
22import java.util.stream.Collectors; 32import java.util.ArrayList;
23 33import java.util.Arrays;
24public class EnigmaDumper implements Dumper { 34import java.util.Collection;
25 private int outputCount = 0; 35import java.util.HashMap;
26 private int indent; 36import java.util.List;
27 private boolean atStart = true; 37import java.util.Map;
28 private boolean pendingCR = false; 38
29 private final StringBuilder sb = new StringBuilder(); 39public class EnigmaDumper extends StringStreamDumper {
30 private final TypeUsageInformation typeUsageInformation; 40 private final StringBuilder sb;
31 private final Set<JavaTypeInstance> emitted = SetFactory.newSet(); 41 private final SourceSettings sourceSettings;
32 private final SourceIndex index = new SourceIndex(); 42 private final SourceIndex index;
33 private int position; 43 private final @Nullable EntryRemapper mapper;
34 44 private final Map<Object, Entry<?>> refs = new HashMap<>();
35 45 private final TypeUsageInformation typeUsage;
36 public EnigmaDumper(TypeUsageInformation typeUsageInformation) { 46 private final MovableDumperContext dumperContext;
37 this.typeUsageInformation = typeUsageInformation; 47 private boolean muteLine = false;
38 } 48
39 49 public EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options,
40 private void append(String s) { 50 @Nullable EntryRemapper mapper) {
41 sb.append(s); 51 this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext());
42 position += s.length(); 52 }
43 } 53
44 54 protected EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options,
45 private String getDesc(JavaTypeInstance type) { 55 @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) {
46 if (!type.isUsableType() && type != RawJavaType.VOID) { 56 super((m, e) -> {
47 throw new IllegalArgumentException(type.toString()); 57 }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context);
48 } 58 this.sb = sb;
49 59 this.sourceSettings = sourceSettings;
50 if (type instanceof JavaGenericBaseInstance) { 60 this.typeUsage = typeUsage;
51 return getDesc(type.getDeGenerifiedType()); 61 this.mapper = mapper;
52 } 62 this.dumperContext = context;
53 63 this.index = index;
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 } 64 }
89 65
90 private MethodEntry getMethodEntry(MethodPrototype method) { 66 private MethodEntry getMethodEntry(MethodPrototype method) {
@@ -92,25 +68,24 @@ public class EnigmaDumper implements Dumper {
92 return null; 68 return null;
93 } 69 }
94 70
95 MethodDescriptor desc = new MethodDescriptor( 71 MethodDescriptor desc = new MethodDescriptor(method.getOriginalDescriptor());
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 72
100 return new MethodEntry(getClassEntry(method.getClassType()), method.getName(), desc); 73 return new MethodEntry(getClassEntry(method.getClassType()), method.getName(), desc);
101 } 74 }
102 75
103 private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) { 76 private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) {
104 int variableIndex = method.isInstanceMethod() ? 1 : 0; 77 MethodEntry owner = getMethodEntry(method);
105 for (int i = 0; i < parameterIndex; i++) { 78 if (owner == null) {
106 variableIndex += method.getArgs().get(i).getStackType().getComputationCategory(); 79 return null;
107 } 80 }
108 81
109 return new LocalVariableEntry(getMethodEntry(method), variableIndex, name, true, null); 82 int variableIndex = method.getParameterLValues().get(parameterIndex).localVariable.getIdx();
83
84 return new LocalVariableEntry(owner, variableIndex, name, true, null);
110 } 85 }
111 86
112 private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, JavaTypeInstance type) { 87 private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, String desc) {
113 return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(getDesc(type))); 88 return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(desc));
114 } 89 }
115 90
116 private ClassEntry getClassEntry(JavaTypeInstance type) { 91 private ClassEntry getClassEntry(JavaTypeInstance type) {
@@ -118,79 +93,144 @@ public class EnigmaDumper implements Dumper {
118 } 93 }
119 94
120 @Override 95 @Override
121 public Dumper beginBlockComment(boolean inline) { 96 public Dumper packageName(JavaRefTypeInstance t) {
122 print("/*").newln(); 97 if (sourceSettings.removeImports) {
123 return this; 98 return this;
99 }
100 return super.packageName(t);
124 } 101 }
125 102
126 @Override 103 @Override
127 public Dumper endBlockComment() { 104 public Dumper keyword(String s) {
128 print(" */").newln(); 105 if (sourceSettings.removeImports && s.startsWith("import")) {
129 return this; 106 muteLine = true;
107 return this;
108 }
109 return super.keyword(s);
130 } 110 }
131 111
132 @Override 112 @Override
133 public Dumper label(String s, boolean inline) { 113 public Dumper endCodeln() {
134 processPendingCR(); 114 if (muteLine) {
135 append(s); 115 muteLine = false;
136 append(":"); 116 return this;
137 return this; 117 }
118 return super.endCodeln();
138 } 119 }
139 120
140 @Override 121 @Override
141 public Dumper comment(String s) { 122 public Dumper print(String s) {
142 append("// "); 123 if (muteLine) {
143 append(s); 124 return this;
144 append("\n"); 125 }
126 return super.print(s);
127 }
128
129 @Override
130 public Dumper dumpClassDoc(JavaTypeInstance owner) {
131 if (mapper != null) {
132 EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner));
133 if (mapping != null) {
134 String javadoc = mapping.getJavadoc();
135 if (javadoc != null) {
136 print("/**").newln();
137 for (String line : javadoc.split("\\R")) {
138 print(" * ").print(line).newln();
139 }
140 print(" */").newln();
141 }
142 }
143 }
145 return this; 144 return this;
146 } 145 }
147 146
148 @Override 147 @Override
149 public void enqueuePendingCarriageReturn() { 148 public Dumper dumpMethodDoc(MethodPrototype method) {
150 pendingCR = true; 149 if (mapper != null) {
151 } 150 List<String> lines = new ArrayList<>();
151 MethodEntry methodEntry = getMethodEntry(method);
152 EntryMapping mapping = mapper.getDeobfMapping(methodEntry);
153 if (mapping != null) {
154 String javadoc = mapping.getJavadoc();
155 if (javadoc != null) {
156 lines.addAll(Arrays.asList(javadoc.split("\\R")));
157 }
158 }
152 159
153 @Override 160 Collection<Entry<?>> children = mapper.getObfChildren(methodEntry);
154 public Dumper removePendingCarriageReturn() { 161
155 pendingCR = false; 162 if (children != null && !children.isEmpty()) {
156 return this; 163 for (Entry<?> each : children) {
157 } 164 if (each instanceof LocalVariableEntry) {
165 EntryMapping paramMapping = mapper.getDeobfMapping(each);
166 if (paramMapping != null) {
167 String javadoc = paramMapping.getJavadoc();
168 if (javadoc != null) {
169 lines.addAll(Arrays.asList(("@param " + paramMapping.getTargetName() + " " + javadoc).split("\\R")));
170 }
171 }
172 }
173 }
174 }
158 175
159 private void processPendingCR() { 176 if (!lines.isEmpty()) {
160 if (pendingCR) { 177 print("/**").newln();
161 append("\n"); 178 for (String line : lines) {
162 atStart = true; 179 print(" * ").print(line).newln();
163 pendingCR = false; 180 }
181 print(" */").newln();
182 }
164 } 183 }
184 return this;
165 } 185 }
166 186
167 @Override 187 @Override
168 public Dumper identifier(String s, Object ref, boolean defines) { 188 public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) {
169 return print(s); 189 if (mapper != null) {
190 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor()));
191 if (mapping != null) {
192 String javadoc = mapping.getJavadoc();
193 if (javadoc != null) {
194 print("/**").newln();
195 for (String line : javadoc.split("\\R")) {
196 print(" * ").print(line).newln();
197 }
198 print(" */").newln();
199 }
200 }
201 }
202 return this;
170 } 203 }
171 204
172 @Override 205 @Override
173 public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) { 206 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); 207 Entry<?> entry = getMethodEntry(method);
208 super.methodName(name, method, special, defines);
209 int now = sb.length();
210 Token token = new Token(now - name.length(), now, name);
177 211
178 if (entry != null) { 212 if (entry != null) {
179 if (defines) { 213 if (defines) {
180 index.addDeclaration(token, entry); 214 index.addDeclaration(token, entry); // override as cfr reuses local vars
181 } else { 215 } else {
182 index.addReference(token, entry, null); 216 index.addReference(token, entry, null);
183 } 217 }
184 } 218 }
185 219
186 return identifier(name, null, defines); 220 return this;
187 } 221 }
188 222
189 @Override 223 @Override
190 public Dumper parameterName(String name, MethodPrototype method, int index, boolean defines) { 224 public Dumper parameterName(String name, Object ref, MethodPrototype method, int index, boolean defines) {
191 doIndent(); 225 super.parameterName(name, ref, method, index, defines);
192 Token token = new Token(position, position + name.length(), name); 226 int now = sb.length();
193 Entry<?> entry = getParameterEntry(method, index, name); 227 Token token = new Token(now - name.length(), now, name);
228 Entry<?> entry;
229 if (defines) {
230 refs.put(ref, entry = getParameterEntry(method, index, name));
231 } else {
232 entry = refs.get(ref);
233 }
194 234
195 if (entry != null) { 235 if (entry != null) {
196 if (defines) { 236 if (defines) {
@@ -200,30 +240,34 @@ public class EnigmaDumper implements Dumper {
200 } 240 }
201 } 241 }
202 242
203 return identifier(name, null, defines); 243 return this;
204 } 244 }
205 245
206 @Override 246 @Override
207 public Dumper variableName(String name, NamedVariable variable, boolean defines) { 247 public Dumper variableName(String name, NamedVariable variable, boolean defines) {
208 return identifier(name, null, defines); 248 // todo catch var declarations in the future
249 return super.variableName(name, variable, defines);
209 } 250 }
210 251
211 @Override 252 @Override
212 public Dumper packageName(JavaRefTypeInstance t) { 253 public Dumper identifier(String name, Object ref, boolean defines) {
213 String s = t.getPackageName(); 254 super.identifier(name, ref, defines);
214 255 Entry<?> entry;
215 if (!s.isEmpty()) { 256 if ((entry = refs.get(ref)) == null) {
216 keyword("package ").print(s).endCodeln().newln(); 257 return this;
217 } 258 }
218 259 int now = sb.length();
260 Token token = new Token(now - name.length(), now, name);
261 index.addReference(token, entry, null);
219 return this; 262 return this;
220 } 263 }
221 264
222 @Override 265 @Override
223 public Dumper fieldName(String name, Field field, JavaTypeInstance owner, boolean hiddenDeclaration, boolean defines) { 266 public Dumper fieldName(String name, Field field, JavaTypeInstance owner, boolean hiddenDeclaration, boolean defines) {
224 doIndent(); 267 super.fieldName(name, field, owner, hiddenDeclaration, defines);
225 Token token = new Token(position, position + name.length(), name); 268 int now = sb.length();
226 Entry<?> entry = field == null ? null : getFieldEntry(owner, name, field.getJavaTypeInstance()); 269 Token token = new Token(now - name.length(), now, name);
270 Entry<?> entry = field == null ? null : getFieldEntry(owner, name, field.getDescriptor());
227 271
228 if (entry != null) { 272 if (entry != null) {
229 if (defines) { 273 if (defines) {
@@ -233,163 +277,54 @@ public class EnigmaDumper implements Dumper {
233 } 277 }
234 } 278 }
235 279
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; 280 return this;
320 } 281 }
321 282
322 @Override 283 @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) { 284 public Dumper dump(JavaTypeInstance type) {
356 return dump(type, TypeContext.None, false); 285 dumpClass(TypeContext.None, type, false);
286 return this;
357 } 287 }
358 288
359 @Override 289 @Override
360 public Dumper dump(JavaTypeInstance type, boolean defines) { 290 public Dumper dump(JavaTypeInstance type, boolean defines) {
361 return dump(type, TypeContext.None, false); 291 dumpClass(TypeContext.None, type, defines);
292 return this;
362 } 293 }
363 294
364 @Override 295 @Override
365 public Dumper dump(JavaTypeInstance type, TypeContext context) { 296 public Dumper dump(JavaTypeInstance type, TypeContext context) {
366 return dump(type, context, false); 297 dumpClass(context, type, false);
298 return this;
367 } 299 }
368 300
369 private Dumper dump(JavaTypeInstance type, TypeContext context, boolean defines) { 301 private void dumpClass(TypeContext context, JavaTypeInstance type, boolean defines) {
370 doIndent();
371 if (type instanceof JavaRefTypeInstance) { 302 if (type instanceof JavaRefTypeInstance) {
372 int start = position; 303 type.dumpInto(this, typeUsage, context);
373 type.dumpInto(this, typeUsageInformation, TypeContext.None); 304 String name = typeUsage.getName(type, context); // the actually used name, dump will indent
374 int end = position; 305 int now = sb.length();
375 Token token = new Token(start, end, sb.toString().substring(start, end)); 306 Token token = new Token(now - name.length(), now, name);
376 307
377 if (defines) { 308 if (defines) {
378 index.addDeclaration(token, getClassEntry(type)); 309 index.addDeclaration(token, getClassEntry(type));
379 } else { 310 } else {
380 index.addReference(token, getClassEntry(type), null); 311 index.addReference(token, getClassEntry(type), null);
381 } 312 }
382 313 return;
383 return this;
384 } 314 }
385 315
386 type.dumpInto(this, typeUsageInformation, context); 316 type.dumpInto(this, typeUsage, context);
387 return this;
388 } 317 }
389 318
319 /**
320 * {@inheritDoc}
321 *
322 * <p>Otherwise the type usage override dumper will not go through the type instance dump
323 * we have here.
324 */
390 @Override 325 @Override
391 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { 326 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
392 return new WithTypeUsageInformationDumper(this, innerclassTypeUsageInformation); 327 return new EnigmaDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext);
393 } 328 }
394 329
395 public SourceIndex getIndex() { 330 public SourceIndex getIndex() {
@@ -401,33 +336,4 @@ public class EnigmaDumper implements Dumper {
401 return sb.toString(); 336 return sb.toString();
402 } 337 }
403 338
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} 339}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
index 9dc1e0a8..bc3d0725 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
@@ -16,7 +16,9 @@ import cuchaz.enigma.source.Source;
16import cuchaz.enigma.source.Decompiler; 16import cuchaz.enigma.source.Decompiler;
17import cuchaz.enigma.source.SourceSettings; 17import cuchaz.enigma.source.SourceSettings;
18import cuchaz.enigma.source.procyon.transformers.*; 18import cuchaz.enigma.source.procyon.transformers.*;
19import cuchaz.enigma.translation.mapping.EntryRemapper;
19import cuchaz.enigma.utils.AsmUtil; 20import cuchaz.enigma.utils.AsmUtil;
21import org.checkerframework.checker.nullness.qual.Nullable;
20import org.objectweb.asm.tree.ClassNode; 22import org.objectweb.asm.tree.ClassNode;
21 23
22public class ProcyonDecompiler implements Decompiler { 24public class ProcyonDecompiler implements Decompiler {
@@ -59,7 +61,7 @@ public class ProcyonDecompiler implements Decompiler {
59 } 61 }
60 62
61 @Override 63 @Override
62 public Source getSource(String className) { 64 public Source getSource(String className, @Nullable EntryRemapper remapper) {
63 TypeReference type = metadataSystem.lookupType(className); 65 TypeReference type = metadataSystem.lookupType(className);
64 if (type == null) { 66 if (type == null) {
65 throw new Error(String.format("Unable to find desc: %s", className)); 67 throw new Error(String.format("Unable to find desc: %s", className));
@@ -85,6 +87,10 @@ public class ProcyonDecompiler implements Decompiler {
85 if (settings.removeVariableFinal) DropVarModifiersAstTransform.INSTANCE.run(source); 87 if (settings.removeVariableFinal) DropVarModifiersAstTransform.INSTANCE.run(source);
86 source.acceptVisitor(new InsertParenthesesVisitor(), null); 88 source.acceptVisitor(new InsertParenthesesVisitor(), null);
87 89
90 if (remapper != null) {
91 new AddJavadocsAstTransform(remapper).run(source);
92 }
93
88 return new ProcyonSource(source, decompilerSettings); 94 return new ProcyonSource(source, decompilerSettings);
89 } 95 }
90 96
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
index 53c8c70b..5b5b70e8 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
@@ -41,7 +41,7 @@ public class ProcyonSource implements Source {
41 } 41 }
42 42
43 @Override 43 @Override
44 public Source addJavadocs(EntryRemapper remapper) { 44 public Source withJavadocs(EntryRemapper remapper) {
45 CompilationUnit remappedTree = (CompilationUnit) tree.clone(); 45 CompilationUnit remappedTree = (CompilationUnit) tree.clone();
46 new AddJavadocsAstTransform(remapper).run(remappedTree); 46 new AddJavadocsAstTransform(remapper).run(remappedTree);
47 return new ProcyonSource(remappedTree, settings); 47 return new ProcyonSource(remappedTree, settings);