diff options
| author | 2021-01-21 19:09:56 -0600 | |
|---|---|---|
| committer | 2021-01-23 10:16:16 -0600 | |
| commit | abcd9a8fdabc2e5a101b772b534ca900d469374b (patch) | |
| tree | 54326a50291135f64b95d78bb3450228719eb642 | |
| parent | Attempt fixing rare CME when loading a jar while tabs are open (diff) | |
| download | enigma-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>
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 | ||
| 5 | repositories.mavenLocal() | ||
| 6 | repositories.jcenter() | ||
| 7 | |||
| 5 | dependencies { | 8 | dependencies { |
| 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 @@ | |||
| 1 | package cuchaz.enigma.source; | 1 | package cuchaz.enigma.source; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 4 | import org.checkerframework.checker.nullness.qual.Nullable; | ||
| 5 | |||
| 3 | public interface Decompiler { | 6 | public 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; | |||
| 5 | public interface Source { | 5 | public 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; | |||
| 4 | import cuchaz.enigma.source.Decompiler; | 4 | import cuchaz.enigma.source.Decompiler; |
| 5 | import cuchaz.enigma.source.Source; | 5 | import cuchaz.enigma.source.Source; |
| 6 | import cuchaz.enigma.source.SourceSettings; | 6 | import cuchaz.enigma.source.SourceSettings; |
| 7 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 7 | import cuchaz.enigma.utils.AsmUtil; | 8 | import cuchaz.enigma.utils.AsmUtil; |
| 8 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; | 9 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; |
| 9 | import org.benf.cfr.reader.apiunreleased.JarContent; | 10 | import org.benf.cfr.reader.apiunreleased.JarContent; |
| @@ -19,6 +20,7 @@ import org.benf.cfr.reader.util.CannotLoadClassException; | |||
| 19 | import org.benf.cfr.reader.util.collections.ListFactory; | 20 | import org.benf.cfr.reader.util.collections.ListFactory; |
| 20 | import org.benf.cfr.reader.util.getopt.Options; | 21 | import org.benf.cfr.reader.util.getopt.Options; |
| 21 | import org.benf.cfr.reader.util.getopt.OptionsImpl; | 22 | import org.benf.cfr.reader.util.getopt.OptionsImpl; |
| 23 | import org.checkerframework.checker.nullness.qual.Nullable; | ||
| 22 | import org.objectweb.asm.tree.ClassNode; | 24 | import org.objectweb.asm.tree.ClassNode; |
| 23 | 25 | ||
| 24 | import java.util.Collection; | 26 | import java.util.Collection; |
| @@ -27,6 +29,8 @@ import java.util.Map; | |||
| 27 | 29 | ||
| 28 | public class CfrDecompiler implements Decompiler { | 30 | public 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 | ||
| 3 | import cuchaz.enigma.source.Source; | 3 | import cuchaz.enigma.source.Source; |
| 4 | import cuchaz.enigma.source.SourceIndex; | 4 | import cuchaz.enigma.source.SourceIndex; |
| 5 | import cuchaz.enigma.source.SourceSettings; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryRemapper; | 6 | import cuchaz.enigma.translation.mapping.EntryRemapper; |
| 6 | import org.benf.cfr.reader.entities.ClassFile; | 7 | import org.benf.cfr.reader.entities.ClassFile; |
| 7 | import org.benf.cfr.reader.state.DCCommonState; | 8 | import org.benf.cfr.reader.state.DCCommonState; |
| 8 | import org.benf.cfr.reader.state.TypeUsageInformation; | 9 | import org.benf.cfr.reader.state.TypeUsageInformation; |
| 10 | import org.benf.cfr.reader.util.getopt.Options; | ||
| 11 | import org.checkerframework.checker.nullness.qual.Nullable; | ||
| 9 | 12 | ||
| 10 | public class CfrSource implements Source { | 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | 1 | package cuchaz.enigma.source.cfr; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.source.Token; | ||
| 4 | import cuchaz.enigma.source.SourceIndex; | 3 | import cuchaz.enigma.source.SourceIndex; |
| 4 | import cuchaz.enigma.source.SourceSettings; | ||
| 5 | import cuchaz.enigma.source.Token; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | 8 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| 6 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 9 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 7 | import cuchaz.enigma.translation.representation.entry.*; | 10 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 8 | import org.benf.cfr.reader.bytecode.analysis.types.*; | 11 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 12 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 15 | import org.benf.cfr.reader.bytecode.analysis.types.JavaArrayTypeInstance; | ||
| 16 | import org.benf.cfr.reader.bytecode.analysis.types.JavaGenericBaseInstance; | ||
| 17 | import org.benf.cfr.reader.bytecode.analysis.types.JavaRefTypeInstance; | ||
| 18 | import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance; | ||
| 19 | import org.benf.cfr.reader.bytecode.analysis.types.MethodPrototype; | ||
| 20 | import org.benf.cfr.reader.bytecode.analysis.types.RawJavaType; | ||
| 9 | import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable; | 21 | import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable; |
| 10 | import org.benf.cfr.reader.entities.Field; | 22 | import org.benf.cfr.reader.entities.Field; |
| 11 | import org.benf.cfr.reader.entities.Method; | ||
| 12 | import org.benf.cfr.reader.mapping.NullMapping; | ||
| 13 | import org.benf.cfr.reader.mapping.ObfuscationMapping; | ||
| 14 | import org.benf.cfr.reader.state.TypeUsageInformation; | 23 | import org.benf.cfr.reader.state.TypeUsageInformation; |
| 15 | import org.benf.cfr.reader.util.collections.SetFactory; | 24 | import org.benf.cfr.reader.util.getopt.Options; |
| 16 | import org.benf.cfr.reader.util.output.DelegatingDumper; | ||
| 17 | import org.benf.cfr.reader.util.output.Dumpable; | ||
| 18 | import org.benf.cfr.reader.util.output.Dumper; | 25 | import org.benf.cfr.reader.util.output.Dumper; |
| 26 | import org.benf.cfr.reader.util.output.IllegalIdentifierDump; | ||
| 27 | import org.benf.cfr.reader.util.output.MovableDumperContext; | ||
| 28 | import org.benf.cfr.reader.util.output.StringStreamDumper; | ||
| 19 | import org.benf.cfr.reader.util.output.TypeContext; | 29 | import org.benf.cfr.reader.util.output.TypeContext; |
| 20 | 30 | import org.checkerframework.checker.nullness.qual.Nullable; | |
| 21 | import java.util.Set; | 31 | |
| 22 | import java.util.stream.Collectors; | 32 | import java.util.ArrayList; |
| 23 | 33 | import java.util.Arrays; | |
| 24 | public class EnigmaDumper implements Dumper { | 34 | import java.util.Collection; |
| 25 | private int outputCount = 0; | 35 | import java.util.HashMap; |
| 26 | private int indent; | 36 | import java.util.List; |
| 27 | private boolean atStart = true; | 37 | import java.util.Map; |
| 28 | private boolean pendingCR = false; | 38 | |
| 29 | private final StringBuilder sb = new StringBuilder(); | 39 | public 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; | |||
| 16 | import cuchaz.enigma.source.Decompiler; | 16 | import cuchaz.enigma.source.Decompiler; |
| 17 | import cuchaz.enigma.source.SourceSettings; | 17 | import cuchaz.enigma.source.SourceSettings; |
| 18 | import cuchaz.enigma.source.procyon.transformers.*; | 18 | import cuchaz.enigma.source.procyon.transformers.*; |
| 19 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 19 | import cuchaz.enigma.utils.AsmUtil; | 20 | import cuchaz.enigma.utils.AsmUtil; |
| 21 | import org.checkerframework.checker.nullness.qual.Nullable; | ||
| 20 | import org.objectweb.asm.tree.ClassNode; | 22 | import org.objectweb.asm.tree.ClassNode; |
| 21 | 23 | ||
| 22 | public class ProcyonDecompiler implements Decompiler { | 24 | public 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); |