From abcd9a8fdabc2e5a101b772b534ca900d469374b Mon Sep 17 00:00:00 2001 From: liach Date: Thu, 21 Jan 2021 19:09:56 -0600 Subject: 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 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 Now renders inner classes properly (and with updated cfr now renders lambda params) Signed-off-by: liach Tweaks to handle purely generic fields and cfr problem classes like ClientEntityManager.Listener Signed-off-by: liach --- enigma/build.gradle | 8 +- .../src/main/java/cuchaz/enigma/EnigmaProject.java | 8 +- .../enigma/classhandle/ClassHandleProvider.java | 2 +- .../main/java/cuchaz/enigma/source/Decompiler.java | 10 +- .../src/main/java/cuchaz/enigma/source/Source.java | 2 +- .../cuchaz/enigma/source/cfr/CfrDecompiler.java | 13 +- .../java/cuchaz/enigma/source/cfr/CfrSource.java | 19 +- .../cuchaz/enigma/source/cfr/EnigmaDumper.java | 494 +++++++++------------ .../enigma/source/procyon/ProcyonDecompiler.java | 8 +- .../enigma/source/procyon/ProcyonSource.java | 2 +- 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 { proGuard } +repositories.mavenLocal() +repositories.jcenter() + dependencies { implementation 'org.ow2.asm:asm:9.0' implementation 'org.ow2.asm:asm-commons:9.0' @@ -9,11 +12,12 @@ dependencies { implementation 'org.ow2.asm:asm-util:9.0' implementation 'net.fabricmc:procyon-fabric-compilertools:0.5.35.13' - implementation 'net.fabricmc:cfr:0.0.1' + implementation 'net.fabricmc:cfr:local' testImplementation 'junit:junit:4.+' testImplementation 'org.hamcrest:hamcrest-all:1.+' - proGuard 'net.sf.proguard:proguard-base:6.+' + proGuard 'com.guardsquare:proguard-base:7.0.1' + // proguard does not yet support java 15. Add -Dorg.gradle.java.home="" to bypass that } // 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 { .filter(Objects::nonNull) .collect(Collectors.toMap(n -> n.name, Functions.identity())); - return new JarExport(compiled); + return new JarExport(mapper, compiled); } public static final class JarExport { + private final EntryRemapper mapper; private final Map compiled; - JarExport(Map compiled) { + JarExport(EntryRemapper mapper, Map compiled) { + this.mapper = mapper; this.compiled = compiled; } @@ -293,7 +295,7 @@ public class EnigmaProject { } private String decompileClass(ClassNode translatedNode, Decompiler decompiler) { - return decompiler.getSource(translatedNode.name).asString(); + return decompiler.getSource(translatedNode.name, mapper).asString(); } } 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 { int v = javadocVersion.incrementAndGet(); return f.thenApplyAsync(res -> { if (res == null || javadocVersion.get() != v) return null; - Result jdSource = res.map(s -> s.addJavadocs(p.project.getMapper())); + Result jdSource = res.map(s -> s.withJavadocs(p.project.getMapper())); withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource)); return jdSource; }, 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 @@ package cuchaz.enigma.source; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import org.checkerframework.checker.nullness.qual.Nullable; + public interface Decompiler { - Source getSource(String className); + @Deprecated // use remapper specific one for easy doc inclusion + default Source getSource(String className) { + return getSource(className, null); + } + + Source getSource(String className, @Nullable EntryRemapper remapper); } 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; public interface Source { String asString(); - Source addJavadocs(EntryRemapper remapper); + Source withJavadocs(EntryRemapper remapper); SourceIndex index(); } 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; import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.Source; import cuchaz.enigma.source.SourceSettings; +import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.utils.AsmUtil; import org.benf.cfr.reader.apiunreleased.ClassFileSource2; import org.benf.cfr.reader.apiunreleased.JarContent; @@ -19,6 +20,7 @@ import org.benf.cfr.reader.util.CannotLoadClassException; import org.benf.cfr.reader.util.collections.ListFactory; import org.benf.cfr.reader.util.getopt.Options; import org.benf.cfr.reader.util.getopt.OptionsImpl; +import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.tree.ClassNode; import java.util.Collection; @@ -27,6 +29,8 @@ import java.util.Map; public class CfrDecompiler implements Decompiler { private final DCCommonState state; + // cfr doesn't add final on params so final setting is ignored + private final SourceSettings settings; public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { Map options = new HashMap<>(); @@ -63,10 +67,12 @@ public class CfrDecompiler implements Decompiler { return new Pair<>(AsmUtil.nodeToBytes(node), path); } }); + + this.settings = sourceSettings; } @Override - public Source getSource(String className) { + public Source getSource(String className, @Nullable EntryRemapper mapper) { DCCommonState state = this.state; Options options = state.getOptions(); @@ -79,7 +85,8 @@ public class CfrDecompiler implements Decompiler { // To make sure we're analysing the cached version try { tree = state.getClassFile(tree.getClassType()); - } catch (CannotLoadClassException ignored) {} + } catch (CannotLoadClassException ignored) { + } if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { tree.loadInnerClasses(state); @@ -91,6 +98,6 @@ public class CfrDecompiler implements Decompiler { TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); tree.analyseTop(state, typeUsageCollector); - return new CfrSource(tree, state, typeUsageCollector.getRealTypeUsageInformation()); + return new CfrSource(settings, tree, state, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); } } 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; import cuchaz.enigma.source.Source; import cuchaz.enigma.source.SourceIndex; +import cuchaz.enigma.source.SourceSettings; import cuchaz.enigma.translation.mapping.EntryRemapper; import org.benf.cfr.reader.entities.ClassFile; import org.benf.cfr.reader.state.DCCommonState; import org.benf.cfr.reader.state.TypeUsageInformation; +import org.benf.cfr.reader.util.getopt.Options; +import org.checkerframework.checker.nullness.qual.Nullable; public class CfrSource implements Source { + private final SourceSettings settings; private final ClassFile tree; private final SourceIndex index; private final String string; + private final DCCommonState state; + private final TypeUsageInformation typeUsage; + private final Options options; - public CfrSource(ClassFile tree, DCCommonState state, TypeUsageInformation typeUsages) { + public CfrSource(SourceSettings settings, ClassFile tree, DCCommonState state, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) { + this.settings = settings; this.tree = tree; + this.state = state; + this.typeUsage = typeUsage; + this.options = options; - EnigmaDumper dumper = new EnigmaDumper(typeUsages); + EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsage, options, mapper); tree.dump(state.getObfuscationMapping().wrap(dumper)); index = dumper.getIndex(); string = dumper.getString(); @@ -27,8 +38,8 @@ public class CfrSource implements Source { } @Override - public Source addJavadocs(EntryRemapper remapper) { - return this; // TODO + public Source withJavadocs(EntryRemapper mapper) { + return new CfrSource(settings, tree, state, typeUsage, options, mapper); } @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 @@ package cuchaz.enigma.source.cfr; -import cuchaz.enigma.source.Token; import cuchaz.enigma.source.SourceIndex; +import cuchaz.enigma.source.SourceSettings; +import cuchaz.enigma.source.Token; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; -import org.benf.cfr.reader.bytecode.analysis.types.*; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; +import org.benf.cfr.reader.bytecode.analysis.types.JavaArrayTypeInstance; +import org.benf.cfr.reader.bytecode.analysis.types.JavaGenericBaseInstance; +import org.benf.cfr.reader.bytecode.analysis.types.JavaRefTypeInstance; +import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance; +import org.benf.cfr.reader.bytecode.analysis.types.MethodPrototype; +import org.benf.cfr.reader.bytecode.analysis.types.RawJavaType; import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable; import org.benf.cfr.reader.entities.Field; -import org.benf.cfr.reader.entities.Method; -import org.benf.cfr.reader.mapping.NullMapping; -import org.benf.cfr.reader.mapping.ObfuscationMapping; import org.benf.cfr.reader.state.TypeUsageInformation; -import org.benf.cfr.reader.util.collections.SetFactory; -import org.benf.cfr.reader.util.output.DelegatingDumper; -import org.benf.cfr.reader.util.output.Dumpable; +import org.benf.cfr.reader.util.getopt.Options; import org.benf.cfr.reader.util.output.Dumper; +import org.benf.cfr.reader.util.output.IllegalIdentifierDump; +import org.benf.cfr.reader.util.output.MovableDumperContext; +import org.benf.cfr.reader.util.output.StringStreamDumper; import org.benf.cfr.reader.util.output.TypeContext; - -import java.util.Set; -import java.util.stream.Collectors; - -public class EnigmaDumper implements Dumper { - private int outputCount = 0; - private int indent; - private boolean atStart = true; - private boolean pendingCR = false; - private final StringBuilder sb = new StringBuilder(); - private final TypeUsageInformation typeUsageInformation; - private final Set emitted = SetFactory.newSet(); - private final SourceIndex index = new SourceIndex(); - private int position; - - - public EnigmaDumper(TypeUsageInformation typeUsageInformation) { - this.typeUsageInformation = typeUsageInformation; - } - - private void append(String s) { - sb.append(s); - position += s.length(); - } - - private String getDesc(JavaTypeInstance type) { - if (!type.isUsableType() && type != RawJavaType.VOID) { - throw new IllegalArgumentException(type.toString()); - } - - if (type instanceof JavaGenericBaseInstance) { - return getDesc(type.getDeGenerifiedType()); - } - - if (type instanceof JavaRefTypeInstance) { - return "L" + type.getRawName().replace('.', '/') + ";"; - } - - if (type instanceof JavaArrayTypeInstance) { - return "[" + getDesc(((JavaArrayTypeInstance) type).removeAnArrayIndirection()); - } - - if (type instanceof RawJavaType) { - switch ((RawJavaType) type) { - case BOOLEAN: - return "Z"; - case BYTE: - return "B"; - case CHAR: - return "C"; - case SHORT: - return "S"; - case INT: - return "I"; - case LONG: - return "J"; - case FLOAT: - return "F"; - case DOUBLE: - return "D"; - case VOID: - return "V"; - default: - throw new AssertionError(); - } - } - - throw new AssertionError(); +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class EnigmaDumper extends StringStreamDumper { + private final StringBuilder sb; + private final SourceSettings sourceSettings; + private final SourceIndex index; + private final @Nullable EntryRemapper mapper; + private final Map> refs = new HashMap<>(); + private final TypeUsageInformation typeUsage; + private final MovableDumperContext dumperContext; + private boolean muteLine = false; + + public EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, + @Nullable EntryRemapper mapper) { + this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext()); + } + + protected EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, + @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) { + super((m, e) -> { + }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context); + this.sb = sb; + this.sourceSettings = sourceSettings; + this.typeUsage = typeUsage; + this.mapper = mapper; + this.dumperContext = context; + this.index = index; } private MethodEntry getMethodEntry(MethodPrototype method) { @@ -92,25 +68,24 @@ public class EnigmaDumper implements Dumper { return null; } - MethodDescriptor desc = new MethodDescriptor( - method.getArgs().stream().map(type -> new TypeDescriptor(getDesc(type))).collect(Collectors.toList()), - new TypeDescriptor(method.getName().equals("") || method.getName().equals("") ? "V" : getDesc(method.getReturnType())) - ); + MethodDescriptor desc = new MethodDescriptor(method.getOriginalDescriptor()); return new MethodEntry(getClassEntry(method.getClassType()), method.getName(), desc); } private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) { - int variableIndex = method.isInstanceMethod() ? 1 : 0; - for (int i = 0; i < parameterIndex; i++) { - variableIndex += method.getArgs().get(i).getStackType().getComputationCategory(); + MethodEntry owner = getMethodEntry(method); + if (owner == null) { + return null; } - return new LocalVariableEntry(getMethodEntry(method), variableIndex, name, true, null); + int variableIndex = method.getParameterLValues().get(parameterIndex).localVariable.getIdx(); + + return new LocalVariableEntry(owner, variableIndex, name, true, null); } - private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, JavaTypeInstance type) { - return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(getDesc(type))); + private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, String desc) { + return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(desc)); } private ClassEntry getClassEntry(JavaTypeInstance type) { @@ -118,79 +93,144 @@ public class EnigmaDumper implements Dumper { } @Override - public Dumper beginBlockComment(boolean inline) { - print("/*").newln(); - return this; + public Dumper packageName(JavaRefTypeInstance t) { + if (sourceSettings.removeImports) { + return this; + } + return super.packageName(t); } @Override - public Dumper endBlockComment() { - print(" */").newln(); - return this; + public Dumper keyword(String s) { + if (sourceSettings.removeImports && s.startsWith("import")) { + muteLine = true; + return this; + } + return super.keyword(s); } @Override - public Dumper label(String s, boolean inline) { - processPendingCR(); - append(s); - append(":"); - return this; + public Dumper endCodeln() { + if (muteLine) { + muteLine = false; + return this; + } + return super.endCodeln(); } @Override - public Dumper comment(String s) { - append("// "); - append(s); - append("\n"); + public Dumper print(String s) { + if (muteLine) { + return this; + } + return super.print(s); + } + + @Override + public Dumper dumpClassDoc(JavaTypeInstance owner) { + if (mapper != null) { + EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner)); + if (mapping != null) { + String javadoc = mapping.getJavadoc(); + if (javadoc != null) { + print("/**").newln(); + for (String line : javadoc.split("\\R")) { + print(" * ").print(line).newln(); + } + print(" */").newln(); + } + } + } return this; } @Override - public void enqueuePendingCarriageReturn() { - pendingCR = true; - } + public Dumper dumpMethodDoc(MethodPrototype method) { + if (mapper != null) { + List lines = new ArrayList<>(); + MethodEntry methodEntry = getMethodEntry(method); + EntryMapping mapping = mapper.getDeobfMapping(methodEntry); + if (mapping != null) { + String javadoc = mapping.getJavadoc(); + if (javadoc != null) { + lines.addAll(Arrays.asList(javadoc.split("\\R"))); + } + } - @Override - public Dumper removePendingCarriageReturn() { - pendingCR = false; - return this; - } + Collection> children = mapper.getObfChildren(methodEntry); + + if (children != null && !children.isEmpty()) { + for (Entry each : children) { + if (each instanceof LocalVariableEntry) { + EntryMapping paramMapping = mapper.getDeobfMapping(each); + if (paramMapping != null) { + String javadoc = paramMapping.getJavadoc(); + if (javadoc != null) { + lines.addAll(Arrays.asList(("@param " + paramMapping.getTargetName() + " " + javadoc).split("\\R"))); + } + } + } + } + } - private void processPendingCR() { - if (pendingCR) { - append("\n"); - atStart = true; - pendingCR = false; + if (!lines.isEmpty()) { + print("/**").newln(); + for (String line : lines) { + print(" * ").print(line).newln(); + } + print(" */").newln(); + } } + return this; } @Override - public Dumper identifier(String s, Object ref, boolean defines) { - return print(s); + public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) { + if (mapper != null) { + EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor())); + if (mapping != null) { + String javadoc = mapping.getJavadoc(); + if (javadoc != null) { + print("/**").newln(); + for (String line : javadoc.split("\\R")) { + print(" * ").print(line).newln(); + } + print(" */").newln(); + } + } + } + return this; } @Override public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) { - doIndent(); - Token token = new Token(position, position + name.length(), name); Entry entry = getMethodEntry(method); + super.methodName(name, method, special, defines); + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); if (entry != null) { if (defines) { - index.addDeclaration(token, entry); + index.addDeclaration(token, entry); // override as cfr reuses local vars } else { index.addReference(token, entry, null); } } - return identifier(name, null, defines); + return this; } @Override - public Dumper parameterName(String name, MethodPrototype method, int index, boolean defines) { - doIndent(); - Token token = new Token(position, position + name.length(), name); - Entry entry = getParameterEntry(method, index, name); + public Dumper parameterName(String name, Object ref, MethodPrototype method, int index, boolean defines) { + super.parameterName(name, ref, method, index, defines); + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + Entry entry; + if (defines) { + refs.put(ref, entry = getParameterEntry(method, index, name)); + } else { + entry = refs.get(ref); + } if (entry != null) { if (defines) { @@ -200,30 +240,34 @@ public class EnigmaDumper implements Dumper { } } - return identifier(name, null, defines); + return this; } @Override public Dumper variableName(String name, NamedVariable variable, boolean defines) { - return identifier(name, null, defines); + // todo catch var declarations in the future + return super.variableName(name, variable, defines); } @Override - public Dumper packageName(JavaRefTypeInstance t) { - String s = t.getPackageName(); - - if (!s.isEmpty()) { - keyword("package ").print(s).endCodeln().newln(); + public Dumper identifier(String name, Object ref, boolean defines) { + super.identifier(name, ref, defines); + Entry entry; + if ((entry = refs.get(ref)) == null) { + return this; } - + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + index.addReference(token, entry, null); return this; } @Override public Dumper fieldName(String name, Field field, JavaTypeInstance owner, boolean hiddenDeclaration, boolean defines) { - doIndent(); - Token token = new Token(position, position + name.length(), name); - Entry entry = field == null ? null : getFieldEntry(owner, name, field.getJavaTypeInstance()); + super.fieldName(name, field, owner, hiddenDeclaration, defines); + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + Entry entry = field == null ? null : getFieldEntry(owner, name, field.getDescriptor()); if (entry != null) { if (defines) { @@ -233,163 +277,54 @@ public class EnigmaDumper implements Dumper { } } - identifier(name, null, defines); - return this; - } - - @Override - public Dumper print(String s) { - processPendingCR(); - doIndent(); - append(s); - atStart = s.endsWith("\n"); - outputCount++; - return this; - } - - @Override - public Dumper print(char c) { - return print(String.valueOf(c)); - } - - @Override - public Dumper newln() { - append("\n"); - atStart = true; - outputCount++; - return this; - } - - @Override - public Dumper endCodeln() { - append(";\n"); - atStart = true; - outputCount++; - return this; - } - - @Override - public Dumper keyword(String s) { - print(s); - return this; - } - - @Override - public Dumper operator(String s) { - print(s); - return this; - } - - @Override - public Dumper separator(String s) { - print(s); - return this; - } - - @Override - public Dumper literal(String s, Object o) { - print(s); - return this; - } - - private void doIndent() { - if (!atStart) return; - String indents = " "; - - for (int x = 0; x < indent; ++x) { - append(indents); - } - - atStart = false; - } - - @Override - public void indent(int diff) { - indent += diff; - } - - @Override - public Dumper dump(Dumpable d) { - if (d == null) { - keyword("null"); - return this; - } - - d.dump(this); return this; } - @Override - public TypeUsageInformation getTypeUsageInformation() { - return typeUsageInformation; - } - - @Override - public ObfuscationMapping getObfuscationMapping() { - return NullMapping.INSTANCE; - } - - @Override - public String toString() { - return sb.toString(); - } - - @Override - public void addSummaryError(Method method, String s) {} - - @Override - public void close() { - } - - @Override - public boolean canEmitClass(JavaTypeInstance type) { - return emitted.add(type); - } - - @Override - public int getOutputCount() { - return outputCount; - } - @Override public Dumper dump(JavaTypeInstance type) { - return dump(type, TypeContext.None, false); + dumpClass(TypeContext.None, type, false); + return this; } @Override public Dumper dump(JavaTypeInstance type, boolean defines) { - return dump(type, TypeContext.None, false); + dumpClass(TypeContext.None, type, defines); + return this; } @Override public Dumper dump(JavaTypeInstance type, TypeContext context) { - return dump(type, context, false); + dumpClass(context, type, false); + return this; } - private Dumper dump(JavaTypeInstance type, TypeContext context, boolean defines) { - doIndent(); + private void dumpClass(TypeContext context, JavaTypeInstance type, boolean defines) { if (type instanceof JavaRefTypeInstance) { - int start = position; - type.dumpInto(this, typeUsageInformation, TypeContext.None); - int end = position; - Token token = new Token(start, end, sb.toString().substring(start, end)); + type.dumpInto(this, typeUsage, context); + String name = typeUsage.getName(type, context); // the actually used name, dump will indent + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); if (defines) { index.addDeclaration(token, getClassEntry(type)); } else { index.addReference(token, getClassEntry(type), null); } - - return this; + return; } - type.dumpInto(this, typeUsageInformation, context); - return this; + type.dumpInto(this, typeUsage, context); } + /** + * {@inheritDoc} + * + *

Otherwise the type usage override dumper will not go through the type instance dump + * we have here. + */ @Override public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { - return new WithTypeUsageInformationDumper(this, innerclassTypeUsageInformation); + return new EnigmaDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext); } public SourceIndex getIndex() { @@ -401,33 +336,4 @@ public class EnigmaDumper implements Dumper { return sb.toString(); } - public static class WithTypeUsageInformationDumper extends DelegatingDumper { - private final TypeUsageInformation typeUsageInformation; - - WithTypeUsageInformationDumper(Dumper delegate, TypeUsageInformation typeUsageInformation) { - super(delegate); - this.typeUsageInformation = typeUsageInformation; - } - - @Override - public TypeUsageInformation getTypeUsageInformation() { - return typeUsageInformation; - } - - @Override - public Dumper dump(JavaTypeInstance javaTypeInstance) { - return dump(javaTypeInstance, TypeContext.None); - } - - @Override - public Dumper dump(JavaTypeInstance javaTypeInstance, TypeContext typeContext) { - javaTypeInstance.dumpInto(this, typeUsageInformation, typeContext); - return this; - } - - @Override - public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { - return new WithTypeUsageInformationDumper(delegate, innerclassTypeUsageInformation); - } - } } 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; import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.SourceSettings; import cuchaz.enigma.source.procyon.transformers.*; +import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.utils.AsmUtil; +import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.tree.ClassNode; public class ProcyonDecompiler implements Decompiler { @@ -59,7 +61,7 @@ public class ProcyonDecompiler implements Decompiler { } @Override - public Source getSource(String className) { + public Source getSource(String className, @Nullable EntryRemapper remapper) { TypeReference type = metadataSystem.lookupType(className); if (type == null) { throw new Error(String.format("Unable to find desc: %s", className)); @@ -85,6 +87,10 @@ public class ProcyonDecompiler implements Decompiler { if (settings.removeVariableFinal) DropVarModifiersAstTransform.INSTANCE.run(source); source.acceptVisitor(new InsertParenthesesVisitor(), null); + if (remapper != null) { + new AddJavadocsAstTransform(remapper).run(source); + } + return new ProcyonSource(source, decompilerSettings); } 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 { } @Override - public Source addJavadocs(EntryRemapper remapper) { + public Source withJavadocs(EntryRemapper remapper) { CompilationUnit remappedTree = (CompilationUnit) tree.clone(); new AddJavadocsAstTransform(remapper).run(remappedTree); return new ProcyonSource(remappedTree, settings); -- cgit v1.2.3