summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorGravatar Runemoro2020-03-09 06:04:08 -0400
committerGravatar GitHub2020-03-09 10:04:08 +0000
commit58c0aeb15a65324de08a914dfa62cc68a516a4e3 (patch)
treef45e8141c0864692051149a478c5a0a6bbe68686 /src/main
parentMade Enigma gui translatable (#193) (diff)
downloadenigma-58c0aeb15a65324de08a914dfa62cc68a516a4e3.tar.gz
enigma-58c0aeb15a65324de08a914dfa62cc68a516a4e3.tar.xz
enigma-58c0aeb15a65324de08a914dfa62cc68a516a4e3.zip
CFR support (#192)
* Add decompiler API * Add CFR support
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/cuchaz/enigma/ClassProvider.java (renamed from src/main/java/cuchaz/enigma/CompiledSource.java)2
-rw-r--r--src/main/java/cuchaz/enigma/EnigmaProject.java39
-rw-r--r--src/main/java/cuchaz/enigma/SourceProvider.java119
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassCache.java4
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java240
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Token.java6
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java93
-rw-r--r--src/main/java/cuchaz/enigma/command/DecompileCommand.java25
-rw-r--r--src/main/java/cuchaz/enigma/config/Config.java20
-rw-r--r--src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java5
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java87
-rw-r--r--src/main/java/cuchaz/enigma/gui/RefreshMode.java7
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/MenuBar.java22
-rw-r--r--src/main/java/cuchaz/enigma/source/Decompiler.java5
-rw-r--r--src/main/java/cuchaz/enigma/source/DecompilerService.java11
-rw-r--r--src/main/java/cuchaz/enigma/source/Decompilers.java9
-rw-r--r--src/main/java/cuchaz/enigma/source/Source.java11
-rw-r--r--src/main/java/cuchaz/enigma/source/SourceIndex.java174
-rw-r--r--src/main/java/cuchaz/enigma/source/SourceSettings.java11
-rw-r--r--src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java108
-rw-r--r--src/main/java/cuchaz/enigma/source/cfr/CfrSource.java38
-rw-r--r--src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java427
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/EntryParser.java49
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java81
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java49
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java)31
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java)34
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java)8
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java41
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java (renamed from src/main/java/cuchaz/enigma/analysis/AddJavadocsAstTransform.java)15
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/DropImportAstTransform.java (renamed from src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java (renamed from src/main/java/cuchaz/enigma/analysis/DropVarModifiersAstTransform.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java (renamed from src/main/java/oml/ast/transformers/InvalidIdentifierFix.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java (renamed from src/main/java/oml/ast/transformers/Java8Generics.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java (renamed from src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java (renamed from src/main/java/oml/ast/transformers/RemoveObjectCasts.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java (renamed from src/main/java/oml/ast/transformers/VarargsFixer.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java (renamed from src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java (renamed from src/main/java/cuchaz/enigma/CachingTypeLoader.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java (renamed from src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java)7
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java (renamed from src/main/java/cuchaz/enigma/NoRetryMetadataSystem.java)2
-rw-r--r--src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java (renamed from src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java)2
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java5
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java10
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java5
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java9
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java9
-rw-r--r--src/main/java/cuchaz/enigma/utils/Pair.java26
-rw-r--r--src/main/java/oml/ExtraClasspathTypeLoader.java59
-rw-r--r--src/main/resources/lang/en_us.json1
-rw-r--r--src/main/resources/lang/fr_fr.json1
52 files changed, 1248 insertions, 691 deletions
diff --git a/src/main/java/cuchaz/enigma/CompiledSource.java b/src/main/java/cuchaz/enigma/ClassProvider.java
index fc051d33..2b913792 100644
--- a/src/main/java/cuchaz/enigma/CompiledSource.java
+++ b/src/main/java/cuchaz/enigma/ClassProvider.java
@@ -4,7 +4,7 @@ import org.objectweb.asm.tree.ClassNode;
4 4
5import javax.annotation.Nullable; 5import javax.annotation.Nullable;
6 6
7public interface CompiledSource { 7public interface ClassProvider {
8 @Nullable 8 @Nullable
9 ClassNode getClassNode(String name); 9 ClassNode getClassNode(String name);
10} 10}
diff --git a/src/main/java/cuchaz/enigma/EnigmaProject.java b/src/main/java/cuchaz/enigma/EnigmaProject.java
index fddacccf..78b49b57 100644
--- a/src/main/java/cuchaz/enigma/EnigmaProject.java
+++ b/src/main/java/cuchaz/enigma/EnigmaProject.java
@@ -1,16 +1,13 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import com.google.common.base.Functions; 3import com.google.common.base.Functions;
4import com.strobel.assembler.metadata.ITypeLoader;
5import com.strobel.assembler.metadata.MetadataSystem;
6import com.strobel.decompiler.DecompilerSettings;
7import com.strobel.decompiler.languages.java.ast.CompilationUnit;
8import cuchaz.enigma.analysis.ClassCache; 4import cuchaz.enigma.analysis.ClassCache;
9import cuchaz.enigma.analysis.EntryReference; 5import cuchaz.enigma.analysis.EntryReference;
10import cuchaz.enigma.analysis.index.JarIndex; 6import cuchaz.enigma.analysis.index.JarIndex;
11import cuchaz.enigma.api.service.NameProposalService; 7import cuchaz.enigma.api.service.NameProposalService;
12import cuchaz.enigma.bytecode.translators.SourceFixVisitor; 8import cuchaz.enigma.bytecode.translators.SourceFixVisitor;
13import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; 9import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
10import cuchaz.enigma.source.*;
14import cuchaz.enigma.translation.Translator; 11import cuchaz.enigma.translation.Translator;
15import cuchaz.enigma.translation.mapping.*; 12import cuchaz.enigma.translation.mapping.*;
16import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; 13import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
@@ -25,10 +22,7 @@ import org.objectweb.asm.ClassWriter;
25import org.objectweb.asm.Opcodes; 22import org.objectweb.asm.Opcodes;
26import org.objectweb.asm.tree.ClassNode; 23import org.objectweb.asm.tree.ClassNode;
27 24
28import java.io.BufferedWriter; 25import java.io.*;
29import java.io.IOException;
30import java.io.PrintWriter;
31import java.io.StringWriter;
32import java.nio.file.Files; 26import java.nio.file.Files;
33import java.nio.file.Path; 27import java.nio.file.Path;
34import java.util.Collection; 28import java.util.Collection;
@@ -200,7 +194,7 @@ public class EnigmaProject {
200 } 194 }
201 } 195 }
202 196
203 public SourceExport decompile(ProgressListener progress) { 197 public SourceExport decompile(ProgressListener progress, DecompilerService decompilerService) {
204 Collection<ClassNode> classes = this.compiled.values().stream() 198 Collection<ClassNode> classes = this.compiled.values().stream()
205 .filter(classNode -> classNode.name.indexOf('$') == -1) 199 .filter(classNode -> classNode.name.indexOf('$') == -1)
206 .collect(Collectors.toList()); 200 .collect(Collectors.toList());
@@ -208,18 +202,7 @@ public class EnigmaProject {
208 progress.init(classes.size(), I18n.translate("progress.classes.decompiling")); 202 progress.init(classes.size(), I18n.translate("progress.classes.decompiling"));
209 203
210 //create a common instance outside the loop as mappings shouldn't be changing while this is happening 204 //create a common instance outside the loop as mappings shouldn't be changing while this is happening
211 CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(this.compiled::get); 205 Decompiler decompiler = decompilerService.create(compiled::get, new SourceSettings(false, false));
212
213 //synchronized to make sure the parallelStream doesn't CME with the cache
214 ITypeLoader synchronizedTypeLoader = new SynchronizedTypeLoader(typeLoader);
215
216 MetadataSystem metadataSystem = new NoRetryMetadataSystem(synchronizedTypeLoader);
217
218 //ensures methods are loaded on classload and prevents race conditions
219 metadataSystem.setEagerMethodLoadingEnabled(true);
220
221 DecompilerSettings settings = SourceProvider.createSettings();
222 SourceProvider sourceProvider = new SourceProvider(settings, synchronizedTypeLoader, metadataSystem);
223 206
224 AtomicInteger count = new AtomicInteger(); 207 AtomicInteger count = new AtomicInteger();
225 208
@@ -227,7 +210,7 @@ public class EnigmaProject {
227 .map(translatedNode -> { 210 .map(translatedNode -> {
228 progress.step(count.getAndIncrement(), translatedNode.name); 211 progress.step(count.getAndIncrement(), translatedNode.name);
229 212
230 String source = decompileClass(translatedNode, sourceProvider); 213 String source = decompileClass(translatedNode, decompiler);
231 return new ClassSource(translatedNode.name, source); 214 return new ClassSource(translatedNode.name, source);
232 }) 215 })
233 .collect(Collectors.toList()); 216 .collect(Collectors.toList());
@@ -235,16 +218,8 @@ public class EnigmaProject {
235 return new SourceExport(decompiled); 218 return new SourceExport(decompiled);
236 } 219 }
237 220
238 private String decompileClass(ClassNode translatedNode, SourceProvider sourceProvider) { 221 private String decompileClass(ClassNode translatedNode, Decompiler decompiler) {
239 StringWriter writer = new StringWriter(); 222 return decompiler.getSource(translatedNode.name).asString();
240 try {
241 CompilationUnit sourceTree = sourceProvider.getSources(translatedNode.name);
242 sourceProvider.writeSource(writer, sourceTree);
243 } catch (Throwable t) {
244 t.printStackTrace();
245 t.printStackTrace(new PrintWriter(writer));
246 }
247 return writer.toString();
248 } 223 }
249 } 224 }
250 225
diff --git a/src/main/java/cuchaz/enigma/SourceProvider.java b/src/main/java/cuchaz/enigma/SourceProvider.java
deleted file mode 100644
index d3d30038..00000000
--- a/src/main/java/cuchaz/enigma/SourceProvider.java
+++ /dev/null
@@ -1,119 +0,0 @@
1package cuchaz.enigma;
2
3import com.strobel.assembler.metadata.ITypeLoader;
4import com.strobel.assembler.metadata.MetadataSystem;
5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference;
7import com.strobel.decompiler.DecompilerContext;
8import com.strobel.decompiler.DecompilerSettings;
9import com.strobel.decompiler.PlainTextOutput;
10import com.strobel.decompiler.languages.java.BraceStyle;
11import com.strobel.decompiler.languages.java.JavaFormattingOptions;
12import com.strobel.decompiler.languages.java.JavaOutputVisitor;
13import com.strobel.decompiler.languages.java.ast.AstBuilder;
14import com.strobel.decompiler.languages.java.ast.CompilationUnit;
15import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
16import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
17import cuchaz.enigma.utils.Utils;
18import oml.ast.transformers.*;
19
20import java.io.StringWriter;
21import java.io.Writer;
22import java.lang.ref.WeakReference;
23import java.util.Arrays;
24import java.util.List;
25import java.util.Objects;
26
27public class SourceProvider {
28 private final DecompilerSettings settings;
29
30 private final ITypeLoader typeLoader;
31 private final MetadataSystem metadataSystem;
32
33 private String lastLookUpName;
34 private WeakReference<CompilationUnit> lastDecompiled;
35
36 public SourceProvider(DecompilerSettings settings, ITypeLoader typeLoader, MetadataSystem metadataSystem) {
37 this.settings = settings;
38 this.typeLoader = typeLoader;
39 this.metadataSystem = metadataSystem;
40 }
41
42 public SourceProvider(DecompilerSettings settings, ITypeLoader typeLoader) {
43 this(settings, typeLoader, new NoRetryMetadataSystem(typeLoader));
44 }
45
46 public static DecompilerSettings createSettings() {
47 DecompilerSettings settings = DecompilerSettings.javaDefaults();
48 settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
49 settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
50 settings.setForceExplicitTypeArguments(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
51 settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
52 settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
53
54 JavaFormattingOptions formattingOptions = settings.getJavaFormattingOptions();
55 formattingOptions.ClassBraceStyle = BraceStyle.EndOfLine;
56 formattingOptions.InterfaceBraceStyle = BraceStyle.EndOfLine;
57 formattingOptions.EnumBraceStyle = BraceStyle.EndOfLine;
58
59 return settings;
60 }
61
62 public CompilationUnit getSources(String name) {
63 // Optimization for javadoc-caused decompilations
64 if (Objects.equals(lastLookUpName, name)) {
65 CompilationUnit last = lastDecompiled.get();
66 if (last != null)
67 return last;
68 }
69
70 TypeReference type = metadataSystem.lookupType(name);
71 if (type == null) {
72 throw new Error(String.format("Unable to find desc: %s", name));
73 }
74
75 TypeDefinition resolvedType = type.resolve();
76
77 settings.setTypeLoader(typeLoader);
78
79 // decompile it!
80 DecompilerContext context = new DecompilerContext();
81 context.setCurrentType(resolvedType);
82 context.setSettings(settings);
83
84 AstBuilder builder = new AstBuilder(context);
85 builder.addType(resolvedType);
86 builder.runTransformations(null);
87 runCustomTransforms(builder, context);
88
89 CompilationUnit ret = builder.getCompilationUnit();
90 lastLookUpName = name;
91 lastDecompiled = new WeakReference<>(ret);
92 return ret;
93 }
94
95 public void writeSource(Writer writer, CompilationUnit sourceTree) {
96 // render the AST into source
97 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null);
98 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null);
99 }
100
101 public String writeSourceToString(CompilationUnit sourceTree) {
102 StringWriter writer = new StringWriter();
103 writeSource(writer, sourceTree);
104 return writer.toString();
105 }
106
107 private static void runCustomTransforms(AstBuilder builder, DecompilerContext context) {
108 List<IAstTransform> transformers = Arrays.asList(
109 new ObfuscatedEnumSwitchRewriterTransform(context),
110 new VarargsFixer(context),
111 new RemoveObjectCasts(context),
112 new Java8Generics(),
113 new InvalidIdentifierFix()
114 );
115 for (IAstTransform transform : transformers) {
116 transform.run(builder.getCompilationUnit());
117 }
118 }
119}
diff --git a/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
index 12ef709a..fddd9a8e 100644
--- a/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
+++ b/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
@@ -1,16 +1,17 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import com.strobel.core.Pair;
4import cuchaz.enigma.api.EnigmaPlugin; 3import cuchaz.enigma.api.EnigmaPlugin;
5import cuchaz.enigma.api.EnigmaPluginContext; 4import cuchaz.enigma.api.EnigmaPluginContext;
6import cuchaz.enigma.api.service.JarIndexerService; 5import cuchaz.enigma.api.service.JarIndexerService;
7import cuchaz.enigma.api.service.NameProposalService; 6import cuchaz.enigma.api.service.NameProposalService;
8import cuchaz.enigma.translation.mapping.ResolutionStrategy; 7import cuchaz.enigma.source.DecompilerService;
8import cuchaz.enigma.source.Decompilers;
9import cuchaz.enigma.source.procyon.ProcyonDecompiler;
9import cuchaz.enigma.translation.representation.TypeDescriptor; 10import cuchaz.enigma.translation.representation.TypeDescriptor;
10import cuchaz.enigma.translation.representation.entry.ClassEntry; 11import cuchaz.enigma.translation.representation.entry.ClassEntry;
11import cuchaz.enigma.translation.representation.entry.Entry; 12import cuchaz.enigma.translation.representation.entry.Entry;
12import cuchaz.enigma.translation.representation.entry.FieldEntry; 13import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.translation.representation.entry.MethodEntry; 14import cuchaz.enigma.utils.Pair;
14import org.objectweb.asm.ClassReader; 15import org.objectweb.asm.ClassReader;
15import org.objectweb.asm.ClassVisitor; 16import org.objectweb.asm.ClassVisitor;
16import org.objectweb.asm.FieldVisitor; 17import org.objectweb.asm.FieldVisitor;
@@ -34,7 +35,6 @@ import java.util.List;
34import java.util.Map; 35import java.util.Map;
35import java.util.Optional; 36import java.util.Optional;
36import java.util.Set; 37import java.util.Set;
37import java.util.function.UnaryOperator;
38 38
39public final class BuiltinPlugin implements EnigmaPlugin { 39public final class BuiltinPlugin implements EnigmaPlugin {
40 40
@@ -44,6 +44,7 @@ public final class BuiltinPlugin implements EnigmaPlugin {
44 @Override 44 @Override
45 public void init(EnigmaPluginContext ctx) { 45 public void init(EnigmaPluginContext ctx) {
46 registerEnumNamingService(ctx); 46 registerEnumNamingService(ctx);
47 registerDecompilerServices(ctx);
47 } 48 }
48 49
49 private void registerEnumNamingService(EnigmaPluginContext ctx) { 50 private void registerEnumNamingService(EnigmaPluginContext ctx) {
@@ -54,6 +55,11 @@ public final class BuiltinPlugin implements EnigmaPlugin {
54 ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry))); 55 ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry)));
55 } 56 }
56 57
58 private void registerDecompilerServices(EnigmaPluginContext ctx) {
59 ctx.registerService("enigma:procyon", DecompilerService.TYPE, ctx1 -> Decompilers.PROCYON);
60 ctx.registerService("enigma:cfr", DecompilerService.TYPE, ctx1 -> Decompilers.CFR);
61 }
62
57 private static final class EnumFieldNameFindingVisitor extends ClassVisitor { 63 private static final class EnumFieldNameFindingVisitor extends ClassVisitor {
58 64
59 private ClassEntry clazz; 65 private ClassEntry clazz;
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassCache.java b/src/main/java/cuchaz/enigma/analysis/ClassCache.java
index 8453df19..d97b2041 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassCache.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassCache.java
@@ -3,7 +3,7 @@ package cuchaz.enigma.analysis;
3import com.google.common.cache.Cache; 3import com.google.common.cache.Cache;
4import com.google.common.cache.CacheBuilder; 4import com.google.common.cache.CacheBuilder;
5import com.google.common.collect.ImmutableSet; 5import com.google.common.collect.ImmutableSet;
6import cuchaz.enigma.CompiledSource; 6import cuchaz.enigma.ClassProvider;
7import cuchaz.enigma.ProgressListener; 7import cuchaz.enigma.ProgressListener;
8import cuchaz.enigma.analysis.index.JarIndex; 8import cuchaz.enigma.analysis.index.JarIndex;
9import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; 9import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor;
@@ -22,7 +22,7 @@ import java.util.concurrent.ExecutionException;
22import java.util.concurrent.TimeUnit; 22import java.util.concurrent.TimeUnit;
23import java.util.function.Supplier; 23import java.util.function.Supplier;
24 24
25public final class ClassCache implements AutoCloseable, CompiledSource { 25public final class ClassCache implements AutoCloseable, ClassProvider {
26 private final FileSystem fileSystem; 26 private final FileSystem fileSystem;
27 private final ImmutableSet<String> classNames; 27 private final ImmutableSet<String> classNames;
28 28
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
deleted file mode 100644
index a800f432..00000000
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ /dev/null
@@ -1,240 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap;
18import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.CompilationUnit;
21import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
22import com.strobel.decompiler.languages.java.ast.Identifier;
23import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
24import cuchaz.enigma.gui.SourceRemapper;
25import cuchaz.enigma.translation.mapping.EntryResolver;
26import cuchaz.enigma.translation.mapping.ResolutionStrategy;
27import cuchaz.enigma.translation.representation.entry.Entry;
28
29import javax.annotation.Nullable;
30import java.util.Collection;
31import java.util.List;
32import java.util.Map;
33import java.util.TreeMap;
34import java.util.regex.Pattern;
35import java.util.stream.Collectors;
36
37public class SourceIndex {
38 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
39
40 private String source;
41 private TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference;
42 private Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens;
43 private Map<Entry<?>, Token> declarationToToken;
44 private List<Integer> lineOffsets;
45 private boolean ignoreBadTokens;
46
47 public SourceIndex(String source) {
48 this(source, true);
49 }
50
51 public SourceIndex(String source, boolean ignoreBadTokens) {
52 this.source = source;
53 this.ignoreBadTokens = ignoreBadTokens;
54 this.tokenToReference = new TreeMap<>();
55 this.referenceToTokens = HashMultimap.create();
56 this.declarationToToken = Maps.newHashMap();
57 calculateLineOffsets();
58 }
59
60 public static SourceIndex buildIndex(String sourceString, CompilationUnit sourceTree, boolean ignoreBadTokens) {
61 SourceIndex index = new SourceIndex(sourceString, ignoreBadTokens);
62 sourceTree.acceptVisitor(new SourceIndexVisitor(), index);
63
64 return index;
65 }
66
67 private void calculateLineOffsets() {
68 // count the lines
69 this.lineOffsets = Lists.newArrayList();
70 this.lineOffsets.add(0);
71 for (int i = 0; i < source.length(); i++) {
72 if (source.charAt(i) == '\n') {
73 this.lineOffsets.add(i + 1);
74 }
75 }
76 }
77
78 public SourceIndex remapTo(SourceRemapper.Result result) {
79 SourceIndex remapped = new SourceIndex(result.getSource(), ignoreBadTokens);
80
81 for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) {
82 remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue()));
83 }
84
85 for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) {
86 EntryReference<Entry<?>, Entry<?>> reference = entry.getKey();
87 Collection<Token> oldTokens = entry.getValue();
88
89 Collection<Token> newTokens = oldTokens.stream()
90 .map(result::getRemappedToken)
91 .collect(Collectors.toList());
92
93 remapped.referenceToTokens.putAll(reference, newTokens);
94 }
95
96 for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) {
97 remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue());
98 }
99
100 return remapped;
101 }
102
103 public String getSource() {
104 return this.source;
105 }
106
107 public Token getToken(AstNode node) {
108
109 // get the text of the node
110 String name = "";
111 if (node instanceof Identifier) {
112 name = ((Identifier) node).getName();
113 }
114
115 // get a token for this node's region
116 Region region = node.getRegion();
117 if (region.getBeginLine() == 0 || region.getEndLine() == 0) {
118 // DEBUG
119 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region));
120 return null;
121 }
122 Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source);
123 if (token.start == 0) {
124 // DEBUG
125 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region));
126 return null;
127 }
128
129 if (node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()) {
130 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
131 if (type != null) {
132 name = type.getName();
133 token.end = token.start + name.length();
134 }
135 }
136
137 // DEBUG
138 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
139
140 // Tokens can have $ in name, even for top-level classes
141 //if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) {
142 // // DEBUG
143 // System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name));
144 // return null;
145 //}
146
147 return token;
148 }
149
150 public void addReference(AstNode node, Entry<?> deobfEntry, Entry<?> deobfContext) {
151 Token token = getToken(node);
152 if (token != null) {
153 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
154 this.tokenToReference.put(token, deobfReference);
155 this.referenceToTokens.put(deobfReference, token);
156 }
157 }
158
159 public void addDeclaration(AstNode node, Entry<?> deobfEntry) {
160 Token token = getToken(node);
161 if (token != null) {
162 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text);
163 this.tokenToReference.put(token, reference);
164 this.referenceToTokens.put(reference, token);
165 this.declarationToToken.put(deobfEntry, token);
166 }
167 }
168
169 public Token getReferenceToken(int pos) {
170 Token token = this.tokenToReference.floorKey(new Token(pos, pos, null));
171 if (token != null && token.contains(pos)) {
172 return token;
173 }
174 return null;
175 }
176
177 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) {
178 return this.referenceToTokens.get(deobfReference);
179 }
180
181 @Nullable
182 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) {
183 if (token == null) {
184 return null;
185 }
186 return this.tokenToReference.get(token);
187 }
188
189 public Iterable<Token> referenceTokens() {
190 return this.tokenToReference.keySet();
191 }
192
193 public Iterable<Token> declarationTokens() {
194 return this.declarationToToken.values();
195 }
196
197 public Iterable<Entry<?>> declarations() {
198 return this.declarationToToken.keySet();
199 }
200
201 public Token getDeclarationToken(Entry<?> entry) {
202 return this.declarationToToken.get(entry);
203 }
204
205 public int getLineNumber(int pos) {
206 // line number is 1-based
207 int line = 0;
208 for (Integer offset : this.lineOffsets) {
209 if (offset > pos) {
210 break;
211 }
212 line++;
213 }
214 return line;
215 }
216
217 public int getColumnNumber(int pos) {
218 // column number is 1-based
219 return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1;
220 }
221
222 private int toPos(int line, int col) {
223 // line and col are 1-based
224 return this.lineOffsets.get(line - 1) + col - 1;
225 }
226
227 public void resolveReferences(EntryResolver resolver) {
228 // resolve all the classes in the source references
229 for (Token token : Lists.newArrayList(referenceToTokens.values())) {
230 EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token);
231 EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
232
233 // replace the reference
234 tokenToReference.replace(token, resolvedReference);
235
236 Collection<Token> tokens = referenceToTokens.removeAll(reference);
237 referenceToTokens.putAll(resolvedReference, tokens);
238 }
239 }
240}
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java
index 12e0aa6c..f0155e56 100644
--- a/src/main/java/cuchaz/enigma/analysis/Token.java
+++ b/src/main/java/cuchaz/enigma/analysis/Token.java
@@ -17,12 +17,10 @@ public class Token implements Comparable<Token> {
17 public int end; 17 public int end;
18 public String text; 18 public String text;
19 19
20 public Token(int start, int end, String source) { 20 public Token(int start, int end, String text) {
21 this.start = start; 21 this.start = start;
22 this.end = end; 22 this.end = end;
23 if (source != null) { 23 this.text = text;
24 this.text = source.substring(start, end);
25 }
26 } 24 }
27 25
28 public int getRenameOffset(String to) { 26 public int getRenameOffset(String to) {
diff --git a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
deleted file mode 100644
index c85d97a5..00000000
--- a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
+++ /dev/null
@@ -1,93 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.strobel.componentmodel.Key;
15import com.strobel.decompiler.languages.java.ast.*;
16import com.strobel.decompiler.patterns.Pattern;
17
18import java.io.*;
19import java.nio.charset.Charset;
20
21public class TreeDumpVisitor extends DepthFirstAstVisitor<Void, Void> {
22
23 private File file;
24 private Writer out;
25
26 public TreeDumpVisitor(File file) {
27 this.file = file;
28 }
29
30 @Override
31 public Void visitCompilationUnit(CompilationUnit node, Void ignored) {
32 try {
33 out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"));
34 visitChildren(node, ignored);
35 out.close();
36 return null;
37 } catch (IOException ex) {
38 throw new Error(ex);
39 }
40 }
41
42 @Override
43 protected Void visitChildren(AstNode node, Void ignored) {
44 // show the tree
45 try {
46 out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n");
47 } catch (IOException ex) {
48 throw new Error(ex);
49 }
50
51 // recurse
52 for (final AstNode child : node.getChildren()) {
53 child.acceptVisitor(this, ignored);
54 }
55 return null;
56 }
57
58 private String getText(AstNode node) {
59 if (node instanceof Identifier) {
60 return "\"" + ((Identifier) node).getName() + "\"";
61 }
62 return "";
63 }
64
65 private String dumpUserData(AstNode node) {
66 StringBuilder buf = new StringBuilder();
67 for (Key<?> key : Keys.ALL_KEYS) {
68 Object val = node.getUserData(key);
69 if (val != null) {
70 buf.append(String.format(" [%s=%s]", key, val));
71 }
72 }
73 return buf.toString();
74 }
75
76 private String getIndent(AstNode node) {
77 StringBuilder buf = new StringBuilder();
78 int depth = getDepth(node);
79 for (int i = 0; i < depth; i++) {
80 buf.append("\t");
81 }
82 return buf.toString();
83 }
84
85 private int getDepth(AstNode node) {
86 int depth = -1;
87 while (node != null) {
88 depth++;
89 node = node.getParent();
90 }
91 return depth;
92 }
93}
diff --git a/src/main/java/cuchaz/enigma/command/DecompileCommand.java b/src/main/java/cuchaz/enigma/command/DecompileCommand.java
index bc23d01d..3d15dac6 100644
--- a/src/main/java/cuchaz/enigma/command/DecompileCommand.java
+++ b/src/main/java/cuchaz/enigma/command/DecompileCommand.java
@@ -2,8 +2,12 @@ package cuchaz.enigma.command;
2 2
3import cuchaz.enigma.EnigmaProject; 3import cuchaz.enigma.EnigmaProject;
4import cuchaz.enigma.ProgressListener; 4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.source.DecompilerService;
6import cuchaz.enigma.source.Decompilers;
5 7
8import java.lang.reflect.Field;
6import java.nio.file.Path; 9import java.nio.file.Path;
10import java.util.Locale;
7 11
8public class DecompileCommand extends Command { 12public class DecompileCommand extends Command {
9 13
@@ -13,7 +17,7 @@ public class DecompileCommand extends Command {
13 17
14 @Override 18 @Override
15 public String getUsage() { 19 public String getUsage() {
16 return "<in jar> <out folder> [<mappings file>]"; 20 return "<decompiler> <in jar> <out folder> [<mappings file>]";
17 } 21 }
18 22
19 @Override 23 @Override
@@ -23,16 +27,27 @@ public class DecompileCommand extends Command {
23 27
24 @Override 28 @Override
25 public void run(String... args) throws Exception { 29 public void run(String... args) throws Exception {
26 Path fileJarIn = getReadableFile(getArg(args, 0, "in jar", true)).toPath(); 30 String decompilerName = getArg(args, 1, "decompiler", true);
27 Path fileJarOut = getWritableFolder(getArg(args, 1, "out folder", true)).toPath(); 31 Path fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)).toPath();
28 Path fileMappings = getReadablePath(getArg(args, 2, "mappings file", false)); 32 Path fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)).toPath();
33 Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false));
34
35 DecompilerService decompilerService;
36
37 try {
38 Field decompilerField = Decompilers.class.getField(decompilerName.toUpperCase(Locale.ROOT));
39 decompilerService = (DecompilerService) decompilerField.get(null);
40 } catch (NoSuchFieldException e) {
41 System.err.println("Decompiler not found.");
42 return;
43 }
29 44
30 EnigmaProject project = openProject(fileJarIn, fileMappings); 45 EnigmaProject project = openProject(fileJarIn, fileMappings);
31 46
32 ProgressListener progress = new ConsoleProgressListener(); 47 ProgressListener progress = new ConsoleProgressListener();
33 48
34 EnigmaProject.JarExport jar = project.exportRemappedJar(progress); 49 EnigmaProject.JarExport jar = project.exportRemappedJar(progress);
35 EnigmaProject.SourceExport source = jar.decompile(progress); 50 EnigmaProject.SourceExport source = jar.decompile(progress, decompilerService);
36 51
37 source.write(fileJarOut, progress); 52 source.write(fileJarOut, progress);
38 } 53 }
diff --git a/src/main/java/cuchaz/enigma/config/Config.java b/src/main/java/cuchaz/enigma/config/Config.java
index a00fe2df..15a974c3 100644
--- a/src/main/java/cuchaz/enigma/config/Config.java
+++ b/src/main/java/cuchaz/enigma/config/Config.java
@@ -3,6 +3,8 @@ package cuchaz.enigma.config;
3import com.bulenkov.darcula.DarculaLaf; 3import com.bulenkov.darcula.DarculaLaf;
4import com.google.common.io.Files; 4import com.google.common.io.Files;
5import com.google.gson.*; 5import com.google.gson.*;
6import cuchaz.enigma.source.DecompilerService;
7import cuchaz.enigma.source.Decompilers;
6 8
7import cuchaz.enigma.utils.I18n; 9import cuchaz.enigma.utils.I18n;
8 10
@@ -137,6 +139,19 @@ public class Config {
137 } 139 }
138 } 140 }
139 141
142 public enum Decompiler {
143 PROCYON("Procyon", Decompilers.PROCYON),
144 CFR("CFR", Decompilers.CFR);
145
146 public final DecompilerService service;
147 public final String name;
148
149 Decompiler(String name, DecompilerService service) {
150 this.name = name;
151 this.service = service;
152 }
153 }
154
140 private static final File DIR_HOME = new File(System.getProperty("user.home")); 155 private static final File DIR_HOME = new File(System.getProperty("user.home"));
141 private static final File ENIGMA_DIR = new File(DIR_HOME, ".enigma"); 156 private static final File ENIGMA_DIR = new File(DIR_HOME, ".enigma");
142 private static final File CONFIG_FILE = new File(ENIGMA_DIR, "config.json"); 157 private static final File CONFIG_FILE = new File(ENIGMA_DIR, "config.json");
@@ -167,11 +182,13 @@ public class Config {
167 public Integer lineNumbersBackground; 182 public Integer lineNumbersBackground;
168 public Integer lineNumbersSelected; 183 public Integer lineNumbersSelected;
169 public Integer lineNumbersForeground; 184 public Integer lineNumbersForeground;
170 185
171 public String language = I18n.DEFAULT_LANGUAGE; 186 public String language = I18n.DEFAULT_LANGUAGE;
172 187
173 public LookAndFeel lookAndFeel = LookAndFeel.DEFAULT; 188 public LookAndFeel lookAndFeel = LookAndFeel.DEFAULT;
174 189
190 public Decompiler decompiler = Decompiler.PROCYON;
191
175 private Config() { 192 private Config() {
176 gson = new GsonBuilder() 193 gson = new GsonBuilder()
177 .registerTypeAdapter(Integer.class, new IntSerializer()) 194 .registerTypeAdapter(Integer.class, new IntSerializer())
@@ -217,6 +234,7 @@ public class Config {
217 public void reset() throws IOException { 234 public void reset() throws IOException {
218 this.lookAndFeel = LookAndFeel.DEFAULT; 235 this.lookAndFeel = LookAndFeel.DEFAULT;
219 this.lookAndFeel.apply(this); 236 this.lookAndFeel.apply(this);
237 this.decompiler = Decompiler.PROCYON;
220 this.language = I18n.DEFAULT_LANGUAGE; 238 this.language = I18n.DEFAULT_LANGUAGE;
221 this.saveConfig(); 239 this.saveConfig();
222 } 240 }
diff --git a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
index c1b163db..4d6b5577 100644
--- a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
+++ b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
@@ -3,14 +3,14 @@ package cuchaz.enigma.gui;
3import cuchaz.enigma.EnigmaProject; 3import cuchaz.enigma.EnigmaProject;
4import cuchaz.enigma.EnigmaServices; 4import cuchaz.enigma.EnigmaServices;
5import cuchaz.enigma.analysis.EntryReference; 5import cuchaz.enigma.analysis.EntryReference;
6import cuchaz.enigma.analysis.SourceIndex;
7import cuchaz.enigma.analysis.Token; 6import cuchaz.enigma.analysis.Token;
8import cuchaz.enigma.api.service.NameProposalService; 7import cuchaz.enigma.api.service.NameProposalService;
9import cuchaz.enigma.gui.highlight.TokenHighlightType; 8import cuchaz.enigma.gui.highlight.TokenHighlightType;
9import cuchaz.enigma.source.Decompiler;
10import cuchaz.enigma.source.SourceIndex;
10import cuchaz.enigma.translation.LocalNameGenerator; 11import cuchaz.enigma.translation.LocalNameGenerator;
11import cuchaz.enigma.translation.Translator; 12import cuchaz.enigma.translation.Translator;
12import cuchaz.enigma.translation.mapping.EntryRemapper; 13import cuchaz.enigma.translation.mapping.EntryRemapper;
13import cuchaz.enigma.translation.mapping.EntryResolver;
14import cuchaz.enigma.translation.mapping.ResolutionStrategy; 14import cuchaz.enigma.translation.mapping.ResolutionStrategy;
15import cuchaz.enigma.translation.representation.TypeDescriptor; 15import cuchaz.enigma.translation.representation.TypeDescriptor;
16import cuchaz.enigma.translation.representation.entry.ClassEntry; 16import cuchaz.enigma.translation.representation.entry.ClassEntry;
@@ -19,7 +19,6 @@ import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry;
19 19
20import javax.annotation.Nullable; 20import javax.annotation.Nullable;
21import java.util.*; 21import java.util.*;
22import java.util.stream.Stream;
23 22
24public class DecompiledClassSource { 23public class DecompiledClassSource {
25 private final ClassEntry classEntry; 24 private final ClassEntry classEntry;
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index dc5010cb..25a1057f 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -13,8 +13,9 @@ package cuchaz.enigma.gui;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import com.google.common.util.concurrent.ThreadFactoryBuilder; 15import com.google.common.util.concurrent.ThreadFactoryBuilder;
16import com.strobel.decompiler.languages.java.ast.CompilationUnit; 16import cuchaz.enigma.Enigma;
17import cuchaz.enigma.*; 17import cuchaz.enigma.EnigmaProfile;
18import cuchaz.enigma.EnigmaProject;
18import cuchaz.enigma.analysis.*; 19import cuchaz.enigma.analysis.*;
19import cuchaz.enigma.api.service.ObfuscationTestService; 20import cuchaz.enigma.api.service.ObfuscationTestService;
20import cuchaz.enigma.bytecode.translators.SourceFixVisitor; 21import cuchaz.enigma.bytecode.translators.SourceFixVisitor;
@@ -23,6 +24,7 @@ import cuchaz.enigma.gui.dialog.ProgressDialog;
23import cuchaz.enigma.gui.stats.StatsGenerator; 24import cuchaz.enigma.gui.stats.StatsGenerator;
24import cuchaz.enigma.gui.stats.StatsMember; 25import cuchaz.enigma.gui.stats.StatsMember;
25import cuchaz.enigma.gui.util.History; 26import cuchaz.enigma.gui.util.History;
27import cuchaz.enigma.source.*;
26import cuchaz.enigma.throwables.MappingParseException; 28import cuchaz.enigma.throwables.MappingParseException;
27import cuchaz.enigma.translation.Translator; 29import cuchaz.enigma.translation.Translator;
28import cuchaz.enigma.translation.mapping.*; 30import cuchaz.enigma.translation.mapping.*;
@@ -36,16 +38,16 @@ import cuchaz.enigma.utils.I18n;
36import cuchaz.enigma.utils.ReadableToken; 38import cuchaz.enigma.utils.ReadableToken;
37import cuchaz.enigma.utils.Utils; 39import cuchaz.enigma.utils.Utils;
38import org.objectweb.asm.Opcodes; 40import org.objectweb.asm.Opcodes;
41import org.objectweb.asm.tree.ClassNode;
39 42
40import javax.annotation.Nullable; 43import javax.annotation.Nullable;
41import javax.swing.*; 44import javax.swing.JOptionPane;
42import java.awt.*; 45import java.awt.Desktop;
43import java.awt.event.ItemEvent; 46import java.awt.event.ItemEvent;
44import java.io.*; 47import java.io.*;
45import java.nio.file.Path; 48import java.nio.file.Path;
46import java.util.Collection; 49import java.util.Collection;
47import java.util.List; 50import java.util.List;
48import java.util.Optional;
49import java.util.Set; 51import java.util.Set;
50import java.util.concurrent.CompletableFuture; 52import java.util.concurrent.CompletableFuture;
51import java.util.concurrent.ExecutorService; 53import java.util.concurrent.ExecutorService;
@@ -65,19 +67,23 @@ public class GuiController {
65 public final Enigma enigma; 67 public final Enigma enigma;
66 68
67 public EnigmaProject project; 69 public EnigmaProject project;
68 private SourceProvider sourceProvider; 70 private DecompilerService decompilerService;
71 private Decompiler decompiler;
69 private IndexTreeBuilder indexTreeBuilder; 72 private IndexTreeBuilder indexTreeBuilder;
70 73
71 private Path loadedMappingPath; 74 private Path loadedMappingPath;
72 private MappingFormat loadedMappingFormat; 75 private MappingFormat loadedMappingFormat;
73 76
74 private DecompiledClassSource currentSource; 77 private DecompiledClassSource currentSource;
78 private Source uncommentedSource;
75 79
76 public GuiController(Gui gui, EnigmaProfile profile) { 80 public GuiController(Gui gui, EnigmaProfile profile) {
77 this.gui = gui; 81 this.gui = gui;
78 this.enigma = Enigma.builder() 82 this.enigma = Enigma.builder()
79 .setProfile(profile) 83 .setProfile(profile)
80 .build(); 84 .build();
85
86 decompilerService = Config.getInstance().decompiler.service;
81 } 87 }
82 88
83 public boolean isDirty() { 89 public boolean isDirty() {
@@ -89,19 +95,27 @@ public class GuiController {
89 95
90 return ProgressDialog.runOffThread(gui.getFrame(), progress -> { 96 return ProgressDialog.runOffThread(gui.getFrame(), progress -> {
91 project = enigma.openJar(jarPath, progress); 97 project = enigma.openJar(jarPath, progress);
92
93 indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); 98 indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex());
94 99 decompiler = createDecompiler();
95 CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(project.getClassCache());
96 typeLoader.addVisitor(visitor -> new SourceFixVisitor(Opcodes.ASM5, visitor, project.getJarIndex()));
97 sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader);
98
99 gui.onFinishOpenJar(jarPath.getFileName().toString()); 100 gui.onFinishOpenJar(jarPath.getFileName().toString());
100
101 refreshClasses(); 101 refreshClasses();
102 }); 102 });
103 } 103 }
104 104
105 private Decompiler createDecompiler() {
106 return decompilerService.create(name -> {
107 ClassNode node = project.getClassCache().getClassNode(name);
108
109 if (node == null) {
110 return null;
111 }
112
113 ClassNode fixedNode = new ClassNode();
114 node.accept(new SourceFixVisitor(Opcodes.ASM7, fixedNode, project.getJarIndex()));
115 return fixedNode;
116 }, new SourceSettings(true, true));
117 }
118
105 public void closeJar() { 119 public void closeJar() {
106 this.project = null; 120 this.project = null;
107 this.gui.onCloseJar(); 121 this.gui.onCloseJar();
@@ -176,7 +190,7 @@ public class GuiController {
176 190
177 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { 191 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> {
178 EnigmaProject.JarExport jar = project.exportRemappedJar(progress); 192 EnigmaProject.JarExport jar = project.exportRemappedJar(progress);
179 EnigmaProject.SourceExport source = jar.decompile(progress); 193 EnigmaProject.SourceExport source = jar.decompile(progress, decompilerService);
180 194
181 source.write(path, progress); 195 source.write(path, progress);
182 }); 196 });
@@ -210,6 +224,7 @@ public class GuiController {
210 if (this.currentSource == null) { 224 if (this.currentSource == null) {
211 return null; 225 return null;
212 } 226 }
227
213 SourceIndex index = this.currentSource.getIndex(); 228 SourceIndex index = this.currentSource.getIndex();
214 return new ReadableToken( 229 return new ReadableToken(
215 index.getLineNumber(token.start), 230 index.getLineNumber(token.start),
@@ -369,27 +384,27 @@ public class GuiController {
369 } 384 }
370 385
371 private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference) { 386 private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference) {
372 refreshCurrentClass(reference, false); 387 refreshCurrentClass(reference, RefreshMode.MINIMAL);
373 } 388 }
374 389
375 private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference, boolean forceDecomp) { 390 private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference, RefreshMode mode) {
376 if (currentSource != null) { 391 if (currentSource != null) {
377 loadClass(currentSource.getEntry(), () -> { 392 loadClass(currentSource.getEntry(), () -> {
378 if (reference != null) { 393 if (reference != null) {
379 showReference(reference); 394 showReference(reference);
380 } 395 }
381 }, forceDecomp); 396 }, mode);
382 } 397 }
383 } 398 }
384 399
385 private void loadClass(ClassEntry classEntry, Runnable callback) { 400 private void loadClass(ClassEntry classEntry, Runnable callback) {
386 loadClass(classEntry, callback, false); 401 loadClass(classEntry, callback, RefreshMode.MINIMAL);
387 } 402 }
388 403
389 private void loadClass(ClassEntry classEntry, Runnable callback, boolean forceDecomp) { 404 private void loadClass(ClassEntry classEntry, Runnable callback, RefreshMode mode) {
390 ClassEntry targetClass = classEntry.getOutermostClass(); 405 ClassEntry targetClass = classEntry.getOutermostClass();
391 406
392 boolean requiresDecompile = forceDecomp || currentSource == null || !currentSource.getEntry().equals(targetClass); 407 boolean requiresDecompile = mode == RefreshMode.FULL || currentSource == null || !currentSource.getEntry().equals(targetClass);
393 if (requiresDecompile) { 408 if (requiresDecompile) {
394 currentSource = null; // Or the GUI may try to find a nonexistent token 409 currentSource = null; // Or the GUI may try to find a nonexistent token
395 gui.setEditorText(I18n.translate("info_panel.editor.class.decompiling")); 410 gui.setEditorText(I18n.translate("info_panel.editor.class.decompiling"));
@@ -397,8 +412,8 @@ public class GuiController {
397 412
398 DECOMPILER_SERVICE.submit(() -> { 413 DECOMPILER_SERVICE.submit(() -> {
399 try { 414 try {
400 if (requiresDecompile) { 415 if (requiresDecompile || mode == RefreshMode.JAVADOCS) {
401 currentSource = decompileSource(targetClass); 416 currentSource = decompileSource(targetClass, mode == RefreshMode.JAVADOCS);
402 } 417 }
403 418
404 remapSource(project.getMapper().getDeobfuscator()); 419 remapSource(project.getMapper().getDeobfuscator());
@@ -410,21 +425,20 @@ public class GuiController {
410 }); 425 });
411 } 426 }
412 427
413 private DecompiledClassSource decompileSource(ClassEntry targetClass) { 428 private DecompiledClassSource decompileSource(ClassEntry targetClass, boolean onlyRefreshJavadocs) {
414 try { 429 try {
415 CompilationUnit sourceTree = (CompilationUnit) sourceProvider.getSources(targetClass.getFullName()).clone(); 430 if (!onlyRefreshJavadocs || currentSource == null || !currentSource.getEntry().equals(targetClass)) {
416 if (sourceTree == null) { 431 uncommentedSource = decompiler.getSource(targetClass.getFullName());
417 gui.setEditorText(I18n.translate("info_panel.editor.class.not_found") + " " + targetClass);
418 return DecompiledClassSource.text(targetClass, "Unable to find class");
419 } 432 }
420 433
421 DropImportAstTransform.INSTANCE.run(sourceTree); 434 Source source = uncommentedSource.addJavadocs(project.getMapper());
422 DropVarModifiersAstTransform.INSTANCE.run(sourceTree);
423 new AddJavadocsAstTransform(project.getMapper()).run(sourceTree);
424 435
425 String sourceString = sourceProvider.writeSourceToString(sourceTree); 436 if (source == null) {
437 gui.setEditorText(I18n.translate("info_panel.editor.class.not_found") + " " + targetClass);
438 return DecompiledClassSource.text(targetClass, "Unable to find class");
439 }
426 440
427 SourceIndex index = SourceIndex.buildIndex(sourceString, sourceTree, true); 441 SourceIndex index = source.index();
428 index.resolveReferences(project.getMapper().getObfResolver()); 442 index.resolveReferences(project.getMapper().getObfResolver());
429 443
430 return new DecompiledClassSource(targetClass, index); 444 return new DecompiledClassSource(targetClass, index);
@@ -535,7 +549,7 @@ public class GuiController {
535 public void changeDocs(EntryReference<Entry<?>, Entry<?>> reference, String updatedDocs) { 549 public void changeDocs(EntryReference<Entry<?>, Entry<?>> reference, String updatedDocs) {
536 changeDoc(reference.getNameableEntry(), updatedDocs); 550 changeDoc(reference.getNameableEntry(), updatedDocs);
537 551
538 refreshCurrentClass(reference, true); 552 refreshCurrentClass(reference, RefreshMode.JAVADOCS);
539 } 553 }
540 554
541 public void changeDoc(Entry<?> obfEntry, String newDoc) { 555 public void changeDoc(Entry<?> obfEntry, String newDoc) {
@@ -582,4 +596,11 @@ public class GuiController {
582 } 596 }
583 }); 597 });
584 } 598 }
599
600 public void setDecompiler(DecompilerService service) {
601 uncommentedSource = null;
602 decompilerService = service;
603 decompiler = createDecompiler();
604 refreshCurrentClass(null, RefreshMode.FULL);
605 }
585} 606}
diff --git a/src/main/java/cuchaz/enigma/gui/RefreshMode.java b/src/main/java/cuchaz/enigma/gui/RefreshMode.java
new file mode 100644
index 00000000..87cb83b2
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/gui/RefreshMode.java
@@ -0,0 +1,7 @@
1package cuchaz.enigma.gui;
2
3public enum RefreshMode {
4 MINIMAL,
5 JAVADOCS,
6 FULL
7}
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index 50f0849f..185e83cf 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -8,6 +8,7 @@ import cuchaz.enigma.gui.dialog.SearchDialog;
8import cuchaz.enigma.gui.stats.StatsMember; 8import cuchaz.enigma.gui.stats.StatsMember;
9import cuchaz.enigma.translation.mapping.serde.MappingFormat; 9import cuchaz.enigma.translation.mapping.serde.MappingFormat;
10import cuchaz.enigma.utils.I18n; 10import cuchaz.enigma.utils.I18n;
11import cuchaz.enigma.utils.Utils;
11 12
12import javax.swing.*; 13import javax.swing.*;
13import java.awt.*; 14import java.awt.*;
@@ -200,6 +201,27 @@ public class MenuBar extends JMenuBar {
200 item.addActionListener(event -> this.gui.close()); 201 item.addActionListener(event -> this.gui.close());
201 } 202 }
202 } 203 }
204
205 {
206 JMenu menu = new JMenu(I18n.translate("menu.decompiler"));
207 add(menu);
208
209 for (Config.Decompiler decompiler : Config.Decompiler.values()) {
210 JMenuItem label = new JMenuItem(decompiler.name);
211 menu.add(label);
212 label.addActionListener(event -> {
213 gui.getController().setDecompiler(decompiler.service);
214
215 try {
216 Config.getInstance().decompiler = decompiler;
217 Config.getInstance().saveConfig();
218 } catch (IOException e) {
219 throw new RuntimeException(e);
220 }
221 });
222 }
223 }
224
203 { 225 {
204 JMenu menu = new JMenu(I18n.translate("menu.view")); 226 JMenu menu = new JMenu(I18n.translate("menu.view"));
205 this.add(menu); 227 this.add(menu);
diff --git a/src/main/java/cuchaz/enigma/source/Decompiler.java b/src/main/java/cuchaz/enigma/source/Decompiler.java
new file mode 100644
index 00000000..c9666d52
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/Decompiler.java
@@ -0,0 +1,5 @@
1package cuchaz.enigma.source;
2
3public interface Decompiler {
4 Source getSource(String className);
5}
diff --git a/src/main/java/cuchaz/enigma/source/DecompilerService.java b/src/main/java/cuchaz/enigma/source/DecompilerService.java
new file mode 100644
index 00000000..377ccbc1
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/DecompilerService.java
@@ -0,0 +1,11 @@
1package cuchaz.enigma.source;
2
3import cuchaz.enigma.ClassProvider;
4import cuchaz.enigma.api.service.EnigmaService;
5import cuchaz.enigma.api.service.EnigmaServiceType;
6
7public interface DecompilerService extends EnigmaService {
8 EnigmaServiceType<DecompilerService> TYPE = EnigmaServiceType.create("decompiler");
9
10 Decompiler create(ClassProvider classProvider, SourceSettings settings);
11}
diff --git a/src/main/java/cuchaz/enigma/source/Decompilers.java b/src/main/java/cuchaz/enigma/source/Decompilers.java
new file mode 100644
index 00000000..7d154a6a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/Decompilers.java
@@ -0,0 +1,9 @@
1package cuchaz.enigma.source;
2
3import cuchaz.enigma.source.cfr.CfrDecompiler;
4import cuchaz.enigma.source.procyon.ProcyonDecompiler;
5
6public class Decompilers {
7 public static final DecompilerService PROCYON = ProcyonDecompiler::new;
8 public static final DecompilerService CFR = CfrDecompiler::new;
9}
diff --git a/src/main/java/cuchaz/enigma/source/Source.java b/src/main/java/cuchaz/enigma/source/Source.java
new file mode 100644
index 00000000..43c4de0c
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/Source.java
@@ -0,0 +1,11 @@
1package cuchaz.enigma.source;
2
3import cuchaz.enigma.translation.mapping.EntryRemapper;
4
5public interface Source {
6 String asString();
7
8 Source addJavadocs(EntryRemapper remapper);
9
10 SourceIndex index();
11}
diff --git a/src/main/java/cuchaz/enigma/source/SourceIndex.java b/src/main/java/cuchaz/enigma/source/SourceIndex.java
new file mode 100644
index 00000000..6a335ec9
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/SourceIndex.java
@@ -0,0 +1,174 @@
1package cuchaz.enigma.source;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Lists;
5import com.google.common.collect.Maps;
6import com.google.common.collect.Multimap;
7import cuchaz.enigma.analysis.EntryReference;
8import cuchaz.enigma.analysis.Token;
9import cuchaz.enigma.gui.SourceRemapper;
10import cuchaz.enigma.translation.mapping.EntryResolver;
11import cuchaz.enigma.translation.mapping.ResolutionStrategy;
12import cuchaz.enigma.translation.representation.entry.Entry;
13
14import java.util.Collection;
15import java.util.List;
16import java.util.Map;
17import java.util.TreeMap;
18import java.util.stream.Collectors;
19
20public class SourceIndex {
21 private String source;
22 private List<Integer> lineOffsets;
23 private final TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference;
24 private final Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens;
25 private final Map<Entry<?>, Token> declarationToToken;
26
27 public SourceIndex() {
28 tokenToReference = new TreeMap<>();
29 referenceToTokens = HashMultimap.create();
30 declarationToToken = Maps.newHashMap();
31 }
32
33 public SourceIndex(String source) {
34 this();
35 setSource(source);
36 }
37
38 public void setSource(String source) {
39 this.source = source;
40 lineOffsets = Lists.newArrayList();
41 lineOffsets.add(0);
42
43 for (int i = 0; i < this.source.length(); i++) {
44 if (this.source.charAt(i) == '\n') {
45 lineOffsets.add(i + 1);
46 }
47 }
48 }
49
50 public String getSource() {
51 return source;
52 }
53
54 public int getLineNumber(int position) {
55 int line = 0;
56
57 for (int offset : lineOffsets) {
58 if (offset > position) {
59 break;
60 }
61
62 line++;
63 }
64
65 return line;
66 }
67
68 public int getColumnNumber(int position) {
69 return position - lineOffsets.get(getLineNumber(position) - 1) + 1;
70 }
71
72 public int getPosition(int line, int column) {
73 return lineOffsets.get(line - 1) + column - 1;
74 }
75
76 public Iterable<Entry<?>> declarations() {
77 return declarationToToken.keySet();
78 }
79
80 public Iterable<Token> declarationTokens() {
81 return declarationToToken.values();
82 }
83
84 public Token getDeclarationToken(Entry<?> entry) {
85 return declarationToToken.get(entry);
86 }
87
88 public void addDeclaration(Token token, Entry<?> deobfEntry) {
89 if (token != null) {
90 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text);
91 tokenToReference.put(token, reference);
92 referenceToTokens.put(reference, token);
93 declarationToToken.put(deobfEntry, token);
94 }
95 }
96
97 public Iterable<EntryReference<Entry<?>, Entry<?>>> references() {
98 return referenceToTokens.keySet();
99 }
100
101 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) {
102 if (token == null) {
103 return null;
104 }
105
106 return tokenToReference.get(token);
107 }
108
109 public Iterable<Token> referenceTokens() {
110 return tokenToReference.keySet();
111 }
112
113 public Token getReferenceToken(int pos) {
114 Token token = tokenToReference.floorKey(new Token(pos, pos, null));
115
116 if (token != null && token.contains(pos)) {
117 return token;
118 }
119
120 return null;
121 }
122
123 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) {
124 return referenceToTokens.get(deobfReference);
125 }
126
127 public void addReference(Token token, Entry<?> deobfEntry, Entry<?> deobfContext) {
128 if (token != null) {
129 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
130 tokenToReference.put(token, deobfReference);
131 referenceToTokens.put(deobfReference, token);
132 }
133 }
134
135 public void resolveReferences(EntryResolver resolver) {
136 // resolve all the classes in the source references
137 for (Token token : Lists.newArrayList(referenceToTokens.values())) {
138 EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token);
139 EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
140
141 // replace the reference
142 tokenToReference.replace(token, resolvedReference);
143
144 Collection<Token> tokens = referenceToTokens.removeAll(reference);
145 referenceToTokens.putAll(resolvedReference, tokens);
146 }
147 }
148
149 public SourceIndex remapTo(SourceRemapper.Result result) {
150 SourceIndex remapped = new SourceIndex(result.getSource());
151
152 for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) {
153 remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue()));
154 }
155
156 for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) {
157 EntryReference<Entry<?>, Entry<?>> reference = entry.getKey();
158 Collection<Token> oldTokens = entry.getValue();
159
160 Collection<Token> newTokens = oldTokens
161 .stream()
162 .map(result::getRemappedToken)
163 .collect(Collectors.toList());
164
165 remapped.referenceToTokens.putAll(reference, newTokens);
166 }
167
168 for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) {
169 remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue());
170 }
171
172 return remapped;
173 }
174}
diff --git a/src/main/java/cuchaz/enigma/source/SourceSettings.java b/src/main/java/cuchaz/enigma/source/SourceSettings.java
new file mode 100644
index 00000000..f6c68e98
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/SourceSettings.java
@@ -0,0 +1,11 @@
1package cuchaz.enigma.source;
2
3public class SourceSettings {
4 public final boolean removeImports;
5 public final boolean removeVariableFinal;
6
7 public SourceSettings(boolean removeImports, boolean removeVariableFinal) {
8 this.removeImports = removeImports;
9 this.removeVariableFinal = removeVariableFinal;
10 }
11}
diff --git a/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
new file mode 100644
index 00000000..9e37f168
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
@@ -0,0 +1,108 @@
1package cuchaz.enigma.source.cfr;
2
3import com.google.common.io.ByteStreams;
4import cuchaz.enigma.ClassProvider;
5import cuchaz.enigma.source.Decompiler;
6import cuchaz.enigma.source.Source;
7import cuchaz.enigma.source.SourceSettings;
8import org.benf.cfr.reader.apiunreleased.ClassFileSource2;
9import org.benf.cfr.reader.apiunreleased.JarContent;
10import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
11import org.benf.cfr.reader.entities.ClassFile;
12import org.benf.cfr.reader.mapping.MappingFactory;
13import org.benf.cfr.reader.mapping.ObfuscationMapping;
14import org.benf.cfr.reader.relationship.MemberNameResolver;
15import org.benf.cfr.reader.state.DCCommonState;
16import org.benf.cfr.reader.state.TypeUsageCollectingDumper;
17import org.benf.cfr.reader.util.AnalysisType;
18import org.benf.cfr.reader.util.CannotLoadClassException;
19import org.benf.cfr.reader.util.collections.ListFactory;
20import org.benf.cfr.reader.util.getopt.Options;
21import org.benf.cfr.reader.util.getopt.OptionsImpl;
22import org.objectweb.asm.ClassWriter;
23import org.objectweb.asm.tree.ClassNode;
24
25import java.io.IOException;
26import java.io.InputStream;
27import java.util.Collection;
28import java.util.HashMap;
29import java.util.Map;
30
31
32public class CfrDecompiler implements Decompiler {
33 private final DCCommonState state;
34
35 public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) {
36 Map<String, String> options = new HashMap<>();
37
38 state = new DCCommonState(OptionsImpl.getFactory().create(options), new ClassFileSource2() {
39 @Override
40 public JarContent addJarContent(String s, AnalysisType analysisType) {
41 return null;
42 }
43
44 @Override
45 public void informAnalysisRelativePathDetail(String usePath, String classFilePath) {
46
47 }
48
49 @Override
50 public Collection<String> addJar(String jarPath) {
51 return null;
52 }
53
54 @Override
55 public String getPossiblyRenamedPath(String path) {
56 return path;
57 }
58
59 @Override
60 public Pair<byte[], String> getClassFileContent(String path) {
61 ClassNode node = classProvider.getClassNode(path.substring(0, path.lastIndexOf('.')));
62
63 if (node == null) {
64 try (InputStream classResource = CfrDecompiler.class.getClassLoader().getResourceAsStream(path)) {
65 if (classResource != null) {
66 return new Pair<>(ByteStreams.toByteArray(classResource), path);
67 }
68 } catch (IOException ignored) {}
69
70 return null;
71 }
72
73 ClassWriter cw = new ClassWriter(0);
74 node.accept(cw);
75 return new Pair<>(cw.toByteArray(), path);
76 }
77 });
78 }
79
80 @Override
81 public Source getSource(String className) {
82 DCCommonState state = this.state;
83 Options options = state.getOptions();
84
85 ObfuscationMapping mapping = MappingFactory.get(options, state);
86 state = new DCCommonState(state, mapping);
87 ClassFile tree = state.getClassFileMaybePath(className);
88
89 state.configureWith(tree);
90
91 // To make sure we're analysing the cached version
92 try {
93 tree = state.getClassFile(tree.getClassType());
94 } catch (CannotLoadClassException ignored) {}
95
96 if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) {
97 tree.loadInnerClasses(state);
98 }
99
100 if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) {
101 MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes()));
102 }
103
104 TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree);
105 tree.analyseTop(state, typeUsageCollector);
106 return new CfrSource(tree, state, typeUsageCollector.getRealTypeUsageInformation());
107 }
108}
diff --git a/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java b/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
new file mode 100644
index 00000000..d4f2da6a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
@@ -0,0 +1,38 @@
1package cuchaz.enigma.source.cfr;
2
3import cuchaz.enigma.source.Source;
4import cuchaz.enigma.source.SourceIndex;
5import cuchaz.enigma.translation.mapping.EntryRemapper;
6import org.benf.cfr.reader.entities.ClassFile;
7import org.benf.cfr.reader.state.DCCommonState;
8import org.benf.cfr.reader.state.TypeUsageInformation;
9
10public class CfrSource implements Source {
11 private final ClassFile tree;
12 private final SourceIndex index;
13 private final String string;
14
15 public CfrSource(ClassFile tree, DCCommonState state, TypeUsageInformation typeUsages) {
16 this.tree = tree;
17
18 EnigmaDumper dumper = new EnigmaDumper(typeUsages);
19 tree.dump(state.getObfuscationMapping().wrap(dumper));
20 index = dumper.getIndex();
21 string = dumper.getString();
22 }
23
24 @Override
25 public String asString() {
26 return string;
27 }
28
29 @Override
30 public Source addJavadocs(EntryRemapper remapper) {
31 return this; // TODO
32 }
33
34 @Override
35 public SourceIndex index() {
36 return index;
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
new file mode 100644
index 00000000..b9cdbeae
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
@@ -0,0 +1,427 @@
1package cuchaz.enigma.source.cfr;
2
3import cuchaz.enigma.analysis.Token;
4import cuchaz.enigma.source.SourceIndex;
5import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.TypeDescriptor;
7import cuchaz.enigma.translation.representation.entry.*;
8import org.benf.cfr.reader.bytecode.analysis.types.*;
9import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable;
10import org.benf.cfr.reader.entities.Field;
11import org.benf.cfr.reader.entities.Method;
12import org.benf.cfr.reader.mapping.NullMapping;
13import org.benf.cfr.reader.mapping.ObfuscationMapping;
14import org.benf.cfr.reader.state.TypeUsageInformation;
15import org.benf.cfr.reader.util.collections.SetFactory;
16import org.benf.cfr.reader.util.output.DelegatingDumper;
17import org.benf.cfr.reader.util.output.Dumpable;
18import org.benf.cfr.reader.util.output.Dumper;
19import org.benf.cfr.reader.util.output.TypeContext;
20
21import java.util.Set;
22import java.util.stream.Collectors;
23
24public class EnigmaDumper implements Dumper {
25 private int outputCount = 0;
26 private int indent;
27 private boolean atStart = true;
28 private boolean pendingCR = false;
29 private final StringBuilder sb = new StringBuilder();
30 private final TypeUsageInformation typeUsageInformation;
31 private final Set<JavaTypeInstance> emitted = SetFactory.newSet();
32 private final SourceIndex index = new SourceIndex();
33 private int position;
34
35
36 public EnigmaDumper(TypeUsageInformation typeUsageInformation) {
37 this.typeUsageInformation = typeUsageInformation;
38 }
39
40 private void append(String s) {
41 sb.append(s);
42 position += s.length();
43 }
44
45 private String getDesc(JavaTypeInstance type) {
46 type = type.getDeGenerifiedType();
47
48 if (type instanceof JavaRefTypeInstance) {
49 return "L" + type.getRawName().replace('.', '/') + ";";
50 }
51
52 if (type instanceof JavaArrayTypeInstance) {
53 return "[" + getDesc(((JavaArrayTypeInstance) type).removeAnArrayIndirection());
54 }
55
56 if (type instanceof RawJavaType) {
57 switch ((RawJavaType) type) {
58 case BOOLEAN:
59 return "Z";
60 case BYTE:
61 return "B";
62 case CHAR:
63 return "C";
64 case SHORT:
65 return "S";
66 case INT:
67 return "I";
68 case LONG:
69 return "J";
70 case FLOAT:
71 return "F";
72 case DOUBLE:
73 return "D";
74 case VOID:
75 return "V";
76 default:
77 throw new AssertionError();
78 }
79 }
80
81 throw new AssertionError();
82 }
83
84 private MethodEntry getMethodEntry(MethodPrototype method) {
85 if (method.getClassType() == null) {
86 return null;
87 }
88
89 MethodDescriptor desc = new MethodDescriptor(
90 method.getArgs().stream().map(type -> new TypeDescriptor(getDesc(type))).collect(Collectors.toList()),
91 new TypeDescriptor(method.getName().equals("<init>") || method.getName().equals("<clinit>") ? "V" : getDesc(method.getReturnType()))
92 );
93
94 return new MethodEntry(getClassEntry(method.getClassType()), method.getName(), desc);
95 }
96
97 private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) {
98 int variableIndex = method.isInstanceMethod() ? 1 : 0;
99 for (int i = 0; i < parameterIndex; i++) {
100 variableIndex += method.getArgs().get(parameterIndex).getStackType().getComputationCategory();
101 }
102
103 return new LocalVariableEntry(getMethodEntry(method), variableIndex, name, true, null);
104 }
105
106 private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, JavaTypeInstance type) {
107 return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(getDesc(type)));
108 }
109
110 private ClassEntry getClassEntry(JavaTypeInstance type) {
111 return new ClassEntry(type.getRawName().replace('.', '/'));
112 }
113
114 @Override
115 public Dumper beginBlockComment(boolean inline) {
116 print("/*").newln();
117 return this;
118 }
119
120 @Override
121 public Dumper endBlockComment() {
122 print(" */").newln();
123 return this;
124 }
125
126 @Override
127 public Dumper label(String s, boolean inline) {
128 processPendingCR();
129 append(s);
130 append(":");
131 return this;
132 }
133
134 @Override
135 public Dumper comment(String s) {
136 append("// ");
137 append(s);
138 append("\n");
139 return this;
140 }
141
142 @Override
143 public void enqueuePendingCarriageReturn() {
144 pendingCR = true;
145 }
146
147 @Override
148 public Dumper removePendingCarriageReturn() {
149 pendingCR = false;
150 return this;
151 }
152
153 private void processPendingCR() {
154 if (pendingCR) {
155 append("\n");
156 atStart = true;
157 pendingCR = false;
158 }
159 }
160
161 @Override
162 public Dumper identifier(String s, Object ref, boolean defines) {
163 return print(s);
164 }
165
166 @Override
167 public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) {
168 doIndent();
169 Token token = new Token(position, position + name.length(), name);
170 Entry<?> entry = getMethodEntry(method);
171
172 if (entry != null) {
173 if (defines) {
174 index.addDeclaration(token, entry);
175 } else {
176 index.addReference(token, entry, null);
177 }
178 }
179
180 return identifier(name, null, defines);
181 }
182
183 @Override
184 public Dumper parameterName(String name, MethodPrototype method, int index, boolean defines) {
185 doIndent();
186 Token token = new Token(position, position + name.length(), name);
187 Entry<?> entry = getParameterEntry(method, index, name);
188
189 if (entry != null) {
190 if (defines) {
191 this.index.addDeclaration(token, entry);
192 } else {
193 this.index.addReference(token, entry, null);
194 }
195 }
196
197 return identifier(name, null, defines);
198 }
199
200 @Override
201 public Dumper variableName(String name, NamedVariable variable, boolean defines) {
202 return identifier(name, null, defines);
203 }
204
205 @Override
206 public Dumper packageName(JavaRefTypeInstance t) {
207 String s = t.getPackageName();
208
209 if (!s.isEmpty()) {
210 keyword("package ").print(s).endCodeln().newln();
211 }
212
213 return this;
214 }
215
216 @Override
217 public Dumper fieldName(String name, Field field, JavaTypeInstance owner, boolean hiddenDeclaration, boolean defines) {
218 doIndent();
219 Token token = new Token(position, position + name.length(), name);
220 Entry<?> entry = field == null ? null : getFieldEntry(owner, name, field.getJavaTypeInstance());
221
222 if (entry != null) {
223 if (defines) {
224 index.addDeclaration(token, entry);
225 } else {
226 index.addReference(token, entry, null);
227 }
228 }
229
230 identifier(name, null, defines);
231 return this;
232 }
233
234 @Override
235 public Dumper print(String s) {
236 processPendingCR();
237 doIndent();
238 append(s);
239 atStart = s.endsWith("\n");
240 outputCount++;
241 return this;
242 }
243
244 @Override
245 public Dumper print(char c) {
246 return print(String.valueOf(c));
247 }
248
249 @Override
250 public Dumper newln() {
251 append("\n");
252 atStart = true;
253 outputCount++;
254 return this;
255 }
256
257 @Override
258 public Dumper endCodeln() {
259 append(";\n");
260 atStart = true;
261 outputCount++;
262 return this;
263 }
264
265 @Override
266 public Dumper keyword(String s) {
267 print(s);
268 return this;
269 }
270
271 @Override
272 public Dumper operator(String s) {
273 print(s);
274 return this;
275 }
276
277 @Override
278 public Dumper separator(String s) {
279 print(s);
280 return this;
281 }
282
283 @Override
284 public Dumper literal(String s, Object o) {
285 print(s);
286 return this;
287 }
288
289 private void doIndent() {
290 if (!atStart) return;
291 String indents = " ";
292
293 for (int x = 0; x < indent; ++x) {
294 append(indents);
295 }
296
297 atStart = false;
298 }
299
300 @Override
301 public void indent(int diff) {
302 indent += diff;
303 }
304
305 @Override
306 public Dumper dump(Dumpable d) {
307 if (d == null) {
308 keyword("null");
309 return this;
310 }
311
312 d.dump(this);
313 return this;
314 }
315
316 @Override
317 public TypeUsageInformation getTypeUsageInformation() {
318 return typeUsageInformation;
319 }
320
321 @Override
322 public ObfuscationMapping getObfuscationMapping() {
323 return NullMapping.INSTANCE;
324 }
325
326 @Override
327 public String toString() {
328 return sb.toString();
329 }
330
331 @Override
332 public void addSummaryError(Method method, String s) {}
333
334 @Override
335 public void close() {
336 }
337
338 @Override
339 public boolean canEmitClass(JavaTypeInstance type) {
340 return emitted.add(type);
341 }
342
343 @Override
344 public int getOutputCount() {
345 return outputCount;
346 }
347
348 @Override
349 public Dumper dump(JavaTypeInstance type) {
350 return dump(type, TypeContext.None, false);
351 }
352
353 @Override
354 public Dumper dump(JavaTypeInstance type, boolean defines) {
355 return dump(type, TypeContext.None, false);
356 }
357
358 @Override
359 public Dumper dump(JavaTypeInstance type, TypeContext context) {
360 return dump(type, context, false);
361 }
362
363 private Dumper dump(JavaTypeInstance type, TypeContext context, boolean defines) {
364 doIndent();
365 if (type instanceof JavaRefTypeInstance) {
366 int start = position;
367 type.dumpInto(this, typeUsageInformation, TypeContext.None);
368 int end = position;
369 Token token = new Token(start, end, sb.toString().substring(start, end));
370
371 if (defines) {
372 index.addDeclaration(token, getClassEntry(type));
373 } else {
374 index.addReference(token, getClassEntry(type), null);
375 }
376
377 return this;
378 }
379
380 type.dumpInto(this, typeUsageInformation, context);
381 return this;
382 }
383
384 @Override
385 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
386 return new WithTypeUsageInformationDumper(this, innerclassTypeUsageInformation);
387 }
388
389 public SourceIndex getIndex() {
390 index.setSource(getString());
391 return index;
392 }
393
394 public String getString() {
395 return sb.toString();
396 }
397
398 public static class WithTypeUsageInformationDumper extends DelegatingDumper {
399 private final TypeUsageInformation typeUsageInformation;
400
401 WithTypeUsageInformationDumper(Dumper delegate, TypeUsageInformation typeUsageInformation) {
402 super(delegate);
403 this.typeUsageInformation = typeUsageInformation;
404 }
405
406 @Override
407 public TypeUsageInformation getTypeUsageInformation() {
408 return typeUsageInformation;
409 }
410
411 @Override
412 public Dumper dump(JavaTypeInstance javaTypeInstance) {
413 return dump(javaTypeInstance, TypeContext.None);
414 }
415
416 @Override
417 public Dumper dump(JavaTypeInstance javaTypeInstance, TypeContext typeContext) {
418 javaTypeInstance.dumpInto(this, typeUsageInformation, typeContext);
419 return this;
420 }
421
422 @Override
423 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
424 return new WithTypeUsageInformationDumper(delegate, innerclassTypeUsageInformation);
425 }
426 }
427}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java b/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java
new file mode 100644
index 00000000..2fae61a6
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java
@@ -0,0 +1,49 @@
1package cuchaz.enigma.source.procyon;
2
3import com.strobel.assembler.metadata.FieldDefinition;
4import com.strobel.assembler.metadata.MethodDefinition;
5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference;
7import cuchaz.enigma.translation.representation.AccessFlags;
8import cuchaz.enigma.translation.representation.MethodDescriptor;
9import cuchaz.enigma.translation.representation.Signature;
10import cuchaz.enigma.translation.representation.TypeDescriptor;
11import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
12import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
14import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
15
16public class EntryParser {
17 public static FieldDefEntry parse(FieldDefinition definition) {
18 ClassEntry owner = parse(definition.getDeclaringType());
19 TypeDescriptor descriptor = new TypeDescriptor(definition.getErasedSignature());
20 Signature signature = Signature.createTypedSignature(definition.getSignature());
21 AccessFlags access = new AccessFlags(definition.getModifiers());
22 return new FieldDefEntry(owner, definition.getName(), descriptor, signature, access, null);
23 }
24
25 public static ClassDefEntry parse(TypeDefinition def) {
26 String name = def.getInternalName();
27 Signature signature = Signature.createSignature(def.getSignature());
28 AccessFlags access = new AccessFlags(def.getModifiers());
29 ClassEntry superClass = def.getBaseType() != null ? parse(def.getBaseType()) : null;
30 ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(EntryParser::parse).toArray(ClassEntry[]::new);
31 return new ClassDefEntry(name, signature, access, superClass, interfaces);
32 }
33
34 public static ClassEntry parse(TypeReference typeReference) {
35 return new ClassEntry(typeReference.getInternalName());
36 }
37
38 public static MethodDefEntry parse(MethodDefinition definition) {
39 ClassEntry classEntry = parse(definition.getDeclaringType());
40 MethodDescriptor descriptor = new MethodDescriptor(definition.getErasedSignature());
41 Signature signature = Signature.createSignature(definition.getSignature());
42 AccessFlags access = new AccessFlags(definition.getModifiers());
43 return new MethodDefEntry(classEntry, definition.getName(), descriptor, signature, access, null);
44 }
45
46 public static TypeDescriptor parseTypeDescriptor(TypeReference type) {
47 return new TypeDescriptor(type.getErasedSignature());
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java b/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
new file mode 100644
index 00000000..37bc0c86
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
@@ -0,0 +1,81 @@
1package cuchaz.enigma.source.procyon;
2
3import com.strobel.assembler.metadata.ITypeLoader;
4import com.strobel.assembler.metadata.MetadataSystem;
5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference;
7import com.strobel.decompiler.DecompilerContext;
8import com.strobel.decompiler.DecompilerSettings;
9import com.strobel.decompiler.languages.java.BraceStyle;
10import com.strobel.decompiler.languages.java.JavaFormattingOptions;
11import com.strobel.decompiler.languages.java.ast.AstBuilder;
12import com.strobel.decompiler.languages.java.ast.CompilationUnit;
13import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
14import cuchaz.enigma.ClassProvider;
15import cuchaz.enigma.api.EnigmaPluginContext;
16import cuchaz.enigma.source.Source;
17import cuchaz.enigma.source.Decompiler;
18import cuchaz.enigma.source.SourceSettings;
19import cuchaz.enigma.source.procyon.transformers.*;
20import cuchaz.enigma.source.procyon.typeloader.CompiledSourceTypeLoader;
21import cuchaz.enigma.source.procyon.typeloader.NoRetryMetadataSystem;
22import cuchaz.enigma.source.procyon.typeloader.SynchronizedTypeLoader;
23import cuchaz.enigma.utils.Utils;
24
25public class ProcyonDecompiler implements Decompiler {
26 private final SourceSettings settings;
27 private final DecompilerSettings decompilerSettings;
28 private final MetadataSystem metadataSystem;
29
30 public ProcyonDecompiler(ClassProvider classProvider, SourceSettings settings) {
31 ITypeLoader typeLoader = new SynchronizedTypeLoader(new CompiledSourceTypeLoader(classProvider));
32
33 metadataSystem = new NoRetryMetadataSystem(typeLoader);
34 metadataSystem.setEagerMethodLoadingEnabled(true);
35
36 decompilerSettings = DecompilerSettings.javaDefaults();
37 decompilerSettings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
38 decompilerSettings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
39 decompilerSettings.setForceExplicitTypeArguments(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
40 decompilerSettings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
41 decompilerSettings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
42 decompilerSettings.setTypeLoader(typeLoader);
43
44 JavaFormattingOptions formattingOptions = decompilerSettings.getJavaFormattingOptions();
45 formattingOptions.ClassBraceStyle = BraceStyle.EndOfLine;
46 formattingOptions.InterfaceBraceStyle = BraceStyle.EndOfLine;
47 formattingOptions.EnumBraceStyle = BraceStyle.EndOfLine;
48
49 this.settings = settings;
50 }
51
52 @Override
53 public Source getSource(String className) {
54 TypeReference type = metadataSystem.lookupType(className);
55 if (type == null) {
56 throw new Error(String.format("Unable to find desc: %s", className));
57 }
58
59 TypeDefinition resolvedType = type.resolve();
60
61 DecompilerContext context = new DecompilerContext();
62 context.setCurrentType(resolvedType);
63 context.setSettings(decompilerSettings);
64
65 AstBuilder builder = new AstBuilder(context);
66 builder.addType(resolvedType);
67 builder.runTransformations(null);
68 CompilationUnit source = builder.getCompilationUnit();
69
70 new ObfuscatedEnumSwitchRewriterTransform(context).run(source);
71 new VarargsFixer(context).run(source);
72 new RemoveObjectCasts(context).run(source);
73 new Java8Generics().run(source);
74 new InvalidIdentifierFix().run(source);
75 if (settings.removeImports) DropImportAstTransform.INSTANCE.run(source);
76 if (settings.removeVariableFinal) DropVarModifiersAstTransform.INSTANCE.run(source);
77 source.acceptVisitor(new InsertParenthesesVisitor(), null);
78
79 return new ProcyonSource(source, decompilerSettings);
80 }
81}
diff --git a/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java b/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
new file mode 100644
index 00000000..53c8c70b
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
@@ -0,0 +1,49 @@
1package cuchaz.enigma.source.procyon;
2
3import com.strobel.decompiler.DecompilerSettings;
4import com.strobel.decompiler.PlainTextOutput;
5import com.strobel.decompiler.languages.java.JavaOutputVisitor;
6import com.strobel.decompiler.languages.java.ast.CompilationUnit;
7import cuchaz.enigma.source.Source;
8import cuchaz.enigma.source.SourceIndex;
9import cuchaz.enigma.source.procyon.index.SourceIndexVisitor;
10import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform;
11import cuchaz.enigma.translation.mapping.EntryRemapper;
12
13import java.io.StringWriter;
14
15public class ProcyonSource implements Source {
16 private final DecompilerSettings settings;
17 private final CompilationUnit tree;
18 private String string;
19
20 public ProcyonSource(CompilationUnit tree, DecompilerSettings settings) {
21 this.settings = settings;
22 this.tree = tree;
23 }
24
25 @Override
26 public SourceIndex index() {
27 SourceIndex index = new SourceIndex(asString());
28 tree.acceptVisitor(new SourceIndexVisitor(), index);
29 return index;
30 }
31
32 @Override
33 public String asString() {
34 if (string == null) {
35 StringWriter writer = new StringWriter();
36 tree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null);
37 string = writer.toString();
38 }
39
40 return string;
41 }
42
43 @Override
44 public Source addJavadocs(EntryRemapper remapper) {
45 CompilationUnit remappedTree = (CompilationUnit) tree.clone();
46 new AddJavadocsAstTransform(remapper).run(remappedTree);
47 return new ProcyonSource(remappedTree, settings);
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java
index 2a72cb1a..f6eeb159 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java
@@ -9,7 +9,7 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.source.procyon.index;
13 13
14import com.strobel.assembler.metadata.FieldDefinition; 14import com.strobel.assembler.metadata.FieldDefinition;
15import com.strobel.assembler.metadata.MethodDefinition; 15import com.strobel.assembler.metadata.MethodDefinition;
@@ -17,10 +17,9 @@ import com.strobel.assembler.metadata.TypeDefinition;
17import com.strobel.assembler.metadata.TypeReference; 17import com.strobel.assembler.metadata.TypeReference;
18import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 20import cuchaz.enigma.source.SourceIndex;
21import cuchaz.enigma.translation.representation.entry.ClassEntry; 21import cuchaz.enigma.source.procyon.EntryParser;
22import cuchaz.enigma.translation.representation.entry.FieldDefEntry; 22import cuchaz.enigma.translation.representation.entry.*;
23import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
24 23
25public class SourceIndexClassVisitor extends SourceIndexVisitor { 24public class SourceIndexClassVisitor extends SourceIndexVisitor {
26 private ClassDefEntry classEntry; 25 private ClassDefEntry classEntry;
@@ -33,10 +32,10 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
33 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 32 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
34 // is this this class, or a subtype? 33 // is this this class, or a subtype?
35 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 34 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
36 ClassDefEntry classEntry = ClassDefEntry.parse(def); 35 ClassDefEntry classEntry = EntryParser.parse(def);
37 if (!classEntry.equals(this.classEntry)) { 36 if (!classEntry.equals(this.classEntry)) {
38 // it's a subtype, recurse 37 // it's a subtype, recurse
39 index.addDeclaration(node.getNameToken(), classEntry); 38 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), classEntry);
40 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 39 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
41 } 40 }
42 41
@@ -48,7 +47,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
48 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 47 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
49 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 48 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
50 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 49 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
51 index.addReference(node.getIdentifierToken(), classEntry, this.classEntry); 50 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.classEntry);
52 } 51 }
53 52
54 return visitChildren(node, index); 53 return visitChildren(node, index);
@@ -57,31 +56,31 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
57 @Override 56 @Override
58 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 57 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
59 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 58 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
60 MethodDefEntry methodEntry = MethodDefEntry.parse(def); 59 MethodDefEntry methodEntry = EntryParser.parse(def);
61 AstNode tokenNode = node.getNameToken(); 60 AstNode tokenNode = node.getNameToken();
62 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) { 61 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) {
63 // for static initializers, check elsewhere for the token node 62 // for static initializers, check elsewhere for the token node
64 tokenNode = node.getModifiers().firstOrNullObject(); 63 tokenNode = node.getModifiers().firstOrNullObject();
65 } 64 }
66 index.addDeclaration(tokenNode, methodEntry); 65 index.addDeclaration(TokenFactory.createToken(index, tokenNode), methodEntry);
67 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index); 66 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
68 } 67 }
69 68
70 @Override 69 @Override
71 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 70 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
72 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 71 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
73 MethodDefEntry methodEntry = MethodDefEntry.parse(def); 72 MethodDefEntry methodEntry = EntryParser.parse(def);
74 index.addDeclaration(node.getNameToken(), methodEntry); 73 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), methodEntry);
75 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index); 74 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
76 } 75 }
77 76
78 @Override 77 @Override
79 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 78 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
80 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 79 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
81 FieldDefEntry fieldEntry = FieldDefEntry.parse(def); 80 FieldDefEntry fieldEntry = EntryParser.parse(def);
82 assert (node.getVariables().size() == 1); 81 assert (node.getVariables().size() == 1);
83 VariableInitializer variable = node.getVariables().firstOrNullObject(); 82 VariableInitializer variable = node.getVariables().firstOrNullObject();
84 index.addDeclaration(variable.getNameToken(), fieldEntry); 83 index.addDeclaration(TokenFactory.createToken(index, variable.getNameToken()), fieldEntry);
85 return visitChildren(node, index); 84 return visitChildren(node, index);
86 } 85 }
87 86
@@ -89,8 +88,8 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
89 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 88 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
90 // treat enum declarations as field declarations 89 // treat enum declarations as field declarations
91 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 90 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
92 FieldDefEntry fieldEntry = FieldDefEntry.parse(def); 91 FieldDefEntry fieldEntry = EntryParser.parse(def);
93 index.addDeclaration(node.getNameToken(), fieldEntry); 92 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), fieldEntry);
94 return visitChildren(node, index); 93 return visitChildren(node, index);
95 } 94 }
96} 95}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java
index dfe58bad..0e8bc51a 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java
@@ -9,7 +9,7 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.source.procyon.index;
13 13
14import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap; 15import com.google.common.collect.Multimap;
@@ -17,6 +17,8 @@ import com.strobel.assembler.metadata.*;
17import com.strobel.decompiler.ast.Variable; 17import com.strobel.decompiler.ast.Variable;
18import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.source.SourceIndex;
21import cuchaz.enigma.source.procyon.EntryParser;
20import cuchaz.enigma.translation.representation.MethodDescriptor; 22import cuchaz.enigma.translation.representation.MethodDescriptor;
21import cuchaz.enigma.translation.representation.TypeDescriptor; 23import cuchaz.enigma.translation.representation.TypeDescriptor;
22import cuchaz.enigma.translation.representation.entry.*; 24import cuchaz.enigma.translation.representation.entry.*;
@@ -56,7 +58,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
56 tokenNode = node.getTarget(); 58 tokenNode = node.getTarget();
57 } 59 }
58 if (tokenNode != null) { 60 if (tokenNode != null) {
59 index.addReference(tokenNode, methodEntry, this.methodEntry); 61 index.addReference(TokenFactory.createToken(index, tokenNode), methodEntry, this.methodEntry);
60 } 62 }
61 } 63 }
62 64
@@ -78,7 +80,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
78 80
79 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 81 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
80 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); 82 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
81 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); 83 index.addReference(TokenFactory.createToken(index, node.getMemberNameToken()), fieldEntry, this.methodEntry);
82 } 84 }
83 85
84 return visitChildren(node, index); 86 return visitChildren(node, index);
@@ -89,7 +91,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
89 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 91 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
90 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 92 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
91 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 93 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
92 index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry); 94 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.methodEntry);
93 } 95 }
94 96
95 return visitChildren(node, index); 97 return visitChildren(node, index);
@@ -103,15 +105,15 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
103 if (parameterIndex >= 0) { 105 if (parameterIndex >= 0) {
104 MethodDefEntry ownerMethod = methodEntry; 106 MethodDefEntry ownerMethod = methodEntry;
105 if (def.getMethod() instanceof MethodDefinition) { 107 if (def.getMethod() instanceof MethodDefinition) {
106 ownerMethod = MethodDefEntry.parse((MethodDefinition) def.getMethod()); 108 ownerMethod = EntryParser.parse((MethodDefinition) def.getMethod());
107 } 109 }
108 110
109 TypeDescriptor parameterType = TypeDescriptor.parse(def.getParameterType()); 111 TypeDescriptor parameterType = EntryParser.parseTypeDescriptor(def.getParameterType());
110 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, parameterIndex, node.getName(), true, parameterType, null); 112 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, parameterIndex, node.getName(), true, parameterType, null);
111 Identifier identifier = node.getNameToken(); 113 Identifier identifier = node.getNameToken();
112 // cache the argument entry and the identifier 114 // cache the argument entry and the identifier
113 identifierEntryCache.put(identifier.getName(), localVariableEntry); 115 identifierEntryCache.put(identifier.getName(), localVariableEntry);
114 index.addDeclaration(identifier, localVariableEntry); 116 index.addDeclaration(TokenFactory.createToken(index, identifier), localVariableEntry);
115 } 117 }
116 118
117 return visitChildren(node, index); 119 return visitChildren(node, index);
@@ -123,7 +125,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
123 if (ref != null) { 125 if (ref != null) {
124 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 126 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
125 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); 127 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
126 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); 128 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), fieldEntry, this.methodEntry);
127 } else 129 } else
128 this.checkIdentifier(node, index); 130 this.checkIdentifier(node, index);
129 return visitChildren(node, index); 131 return visitChildren(node, index);
@@ -131,7 +133,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
131 133
132 private void checkIdentifier(IdentifierExpression node, SourceIndex index) { 134 private void checkIdentifier(IdentifierExpression node, SourceIndex index) {
133 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! 135 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token!
134 index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier())); 136 index.addDeclaration(TokenFactory.createToken(index, node.getIdentifierToken()), identifierEntryCache.get(node.getIdentifier()));
135 else 137 else
136 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! 138 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it!
137 } 139 }
@@ -143,7 +145,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
143 if (entry == null) 145 if (entry == null)
144 return; 146 return;
145 for (Identifier identifier : unmatchedIdentifier.get(key)) 147 for (Identifier identifier : unmatchedIdentifier.get(key))
146 index.addDeclaration(identifier, entry); 148 index.addDeclaration(TokenFactory.createToken(index, identifier), entry);
147 unmatchedIdentifier.removeAll(key); 149 unmatchedIdentifier.removeAll(key);
148 } 150 }
149 151
@@ -154,7 +156,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
154 SimpleType simpleTypeNode = (SimpleType) node.getType(); 156 SimpleType simpleTypeNode = (SimpleType) node.getType();
155 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 157 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
156 MethodEntry constructorEntry = new MethodEntry(classEntry, "<init>", new MethodDescriptor(ref.getErasedSignature())); 158 MethodEntry constructorEntry = new MethodEntry(classEntry, "<init>", new MethodDescriptor(ref.getErasedSignature()));
157 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry); 159 index.addReference(TokenFactory.createToken(index, simpleTypeNode.getIdentifierToken()), constructorEntry, this.methodEntry);
158 } 160 }
159 161
160 return visitChildren(node, index); 162 return visitChildren(node, index);
@@ -175,12 +177,12 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
175 if (originalVariable != null) { 177 if (originalVariable != null) {
176 int variableIndex = originalVariable.getSlot(); 178 int variableIndex = originalVariable.getSlot();
177 if (variableIndex >= 0) { 179 if (variableIndex >= 0) {
178 MethodDefEntry ownerMethod = MethodDefEntry.parse(originalVariable.getDeclaringMethod()); 180 MethodDefEntry ownerMethod = EntryParser.parse(originalVariable.getDeclaringMethod());
179 TypeDescriptor variableType = TypeDescriptor.parse(originalVariable.getVariableType()); 181 TypeDescriptor variableType = EntryParser.parseTypeDescriptor(originalVariable.getVariableType());
180 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, variableIndex, initializer.getName(), false, variableType, null); 182 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, variableIndex, initializer.getName(), false, variableType, null);
181 identifierEntryCache.put(identifier.getName(), localVariableEntry); 183 identifierEntryCache.put(identifier.getName(), localVariableEntry);
182 addDeclarationToUnmatched(identifier.getName(), index); 184 addDeclarationToUnmatched(identifier.getName(), index);
183 index.addDeclaration(identifier, localVariableEntry); 185 index.addDeclaration(TokenFactory.createToken(index, identifier), localVariableEntry);
184 } 186 }
185 } 187 }
186 } 188 }
@@ -203,11 +205,11 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
203 AstNode targetToken = node.getTarget(); 205 AstNode targetToken = node.getTarget();
204 206
205 if (methodNameToken != null) { 207 if (methodNameToken != null) {
206 index.addReference(methodNameToken, methodEntry, this.methodEntry); 208 index.addReference(TokenFactory.createToken(index, methodNameToken), methodEntry, this.methodEntry);
207 } 209 }
208 210
209 if (targetToken != null && !(targetToken instanceof ThisReferenceExpression)) { 211 if (targetToken != null && !(targetToken instanceof ThisReferenceExpression)) {
210 index.addReference(targetToken, methodEntry.getParent(), this.methodEntry); 212 index.addReference(TokenFactory.createToken(index, targetToken), methodEntry.getParent(), this.methodEntry);
211 } 213 }
212 } 214 }
213 215
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java
index 8bd00a86..dad505f7 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java
@@ -9,21 +9,23 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.source.procyon.index;
13 13
14import com.strobel.assembler.metadata.TypeDefinition; 14import com.strobel.assembler.metadata.TypeDefinition;
15import com.strobel.decompiler.languages.java.ast.AstNode; 15import com.strobel.decompiler.languages.java.ast.AstNode;
16import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; 16import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
17import com.strobel.decompiler.languages.java.ast.Keys; 17import com.strobel.decompiler.languages.java.ast.Keys;
18import com.strobel.decompiler.languages.java.ast.TypeDeclaration; 18import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
19import cuchaz.enigma.source.SourceIndex;
20import cuchaz.enigma.source.procyon.EntryParser;
19import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 21import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
20 22
21public class SourceIndexVisitor extends DepthFirstAstVisitor<SourceIndex, Void> { 23public class SourceIndexVisitor extends DepthFirstAstVisitor<SourceIndex, Void> {
22 @Override 24 @Override
23 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 25 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
24 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 26 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
25 ClassDefEntry classEntry = ClassDefEntry.parse(def); 27 ClassDefEntry classEntry = EntryParser.parse(def);
26 index.addDeclaration(node.getNameToken(), classEntry); 28 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), classEntry);
27 29
28 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 30 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
29 } 31 }
diff --git a/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java b/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java
new file mode 100644
index 00000000..db90ffae
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java
@@ -0,0 +1,41 @@
1package cuchaz.enigma.source.procyon.index;
2
3import com.strobel.decompiler.languages.Region;
4import com.strobel.decompiler.languages.java.ast.AstNode;
5import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
6import com.strobel.decompiler.languages.java.ast.Identifier;
7import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
8import cuchaz.enigma.analysis.Token;
9import cuchaz.enigma.source.SourceIndex;
10
11import java.util.regex.Pattern;
12
13public class TokenFactory {
14 private static final Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
15
16 public static Token createToken(SourceIndex index, AstNode node) {
17 String name = node instanceof Identifier ? ((Identifier) node).getName() : "";
18 Region region = node.getRegion();
19
20 int start = index.getPosition(region.getBeginLine(), region.getBeginColumn());
21 int end = index.getPosition(region.getEndLine(), region.getEndColumn());
22 String text = index.getSource().substring(start, end);
23 Token token = new Token(start, end, text);
24
25 boolean isAnonymousInner =
26 node instanceof Identifier &&
27 name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration &&
28 name.lastIndexOf('$') >= 0 &&
29 !ANONYMOUS_INNER.matcher(name).matches();
30
31 if (isAnonymousInner) {
32 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
33 if (type != null) {
34 name = type.getName();
35 token.end = token.start + name.length();
36 }
37 }
38
39 return token;
40 }
41}
diff --git a/src/main/java/cuchaz/enigma/analysis/AddJavadocsAstTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
index 17ae63df..70fc8c6b 100644
--- a/src/main/java/cuchaz/enigma/analysis/AddJavadocsAstTransform.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
@@ -1,15 +1,14 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.google.common.base.Function; 3import com.google.common.base.Function;
4import com.google.common.base.Strings; 4import com.google.common.base.Strings;
5import com.strobel.assembler.metadata.ParameterDefinition; 5import com.strobel.assembler.metadata.ParameterDefinition;
6import com.strobel.decompiler.languages.java.ast.*; 6import com.strobel.decompiler.languages.java.ast.*;
7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
8import cuchaz.enigma.source.procyon.EntryParser;
8import cuchaz.enigma.translation.mapping.EntryMapping; 9import cuchaz.enigma.translation.mapping.EntryMapping;
9import cuchaz.enigma.translation.mapping.EntryRemapper; 10import cuchaz.enigma.translation.mapping.EntryRemapper;
10import cuchaz.enigma.translation.mapping.EntryResolver;
11import cuchaz.enigma.translation.mapping.ResolutionStrategy; 11import cuchaz.enigma.translation.mapping.ResolutionStrategy;
12import cuchaz.enigma.translation.representation.TypeDescriptor;
13import cuchaz.enigma.translation.representation.entry.*; 12import cuchaz.enigma.translation.representation.entry.*;
14 13
15import java.util.ArrayList; 14import java.util.ArrayList;
@@ -68,7 +67,7 @@ public final class AddJavadocsAstTransform implements IAstTransform {
68 } 67 }
69 68
70 private void visitMethod(AstNode node) { 69 private void visitMethod(AstNode node) {
71 final MethodDefEntry methodDefEntry = MethodDefEntry.parse(node.getUserData(Keys.METHOD_DEFINITION)); 70 final MethodDefEntry methodDefEntry = EntryParser.parse(node.getUserData(Keys.METHOD_DEFINITION));
72 final Comment[] baseComments = getComments(node, $ -> methodDefEntry); 71 final Comment[] baseComments = getComments(node, $ -> methodDefEntry);
73 List<Comment> comments = new ArrayList<>(); 72 List<Comment> comments = new ArrayList<>();
74 if (baseComments != null) 73 if (baseComments != null)
@@ -78,7 +77,7 @@ public final class AddJavadocsAstTransform implements IAstTransform {
78 ParameterDefinition def = dec.getUserData(Keys.PARAMETER_DEFINITION); 77 ParameterDefinition def = dec.getUserData(Keys.PARAMETER_DEFINITION);
79 final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(), 78 final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(),
80 true, 79 true,
81 TypeDescriptor.parse(def.getParameterType()), null)); 80 EntryParser.parseTypeDescriptor(def.getParameterType()), null));
82 if (paramComments != null) 81 if (paramComments != null)
83 Collections.addAll(comments, paramComments); 82 Collections.addAll(comments, paramComments);
84 } 83 }
@@ -116,19 +115,19 @@ public final class AddJavadocsAstTransform implements IAstTransform {
116 115
117 @Override 116 @Override
118 public Void visitFieldDeclaration(FieldDeclaration node, Void data) { 117 public Void visitFieldDeclaration(FieldDeclaration node, Void data) {
119 addDoc(node, dec -> FieldDefEntry.parse(dec.getUserData(Keys.FIELD_DEFINITION))); 118 addDoc(node, dec -> EntryParser.parse(dec.getUserData(Keys.FIELD_DEFINITION)));
120 return super.visitFieldDeclaration(node, data); 119 return super.visitFieldDeclaration(node, data);
121 } 120 }
122 121
123 @Override 122 @Override
124 public Void visitTypeDeclaration(TypeDeclaration node, Void data) { 123 public Void visitTypeDeclaration(TypeDeclaration node, Void data) {
125 addDoc(node, dec -> ClassDefEntry.parse(dec.getUserData(Keys.TYPE_DEFINITION))); 124 addDoc(node, dec -> EntryParser.parse(dec.getUserData(Keys.TYPE_DEFINITION)));
126 return super.visitTypeDeclaration(node, data); 125 return super.visitTypeDeclaration(node, data);
127 } 126 }
128 127
129 @Override 128 @Override
130 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void data) { 129 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void data) {
131 addDoc(node, dec -> FieldDefEntry.parse(dec.getUserData(Keys.FIELD_DEFINITION))); 130 addDoc(node, dec -> EntryParser.parse(dec.getUserData(Keys.FIELD_DEFINITION)));
132 return super.visitEnumValueDeclaration(node, data); 131 return super.visitEnumValueDeclaration(node, data);
133 } 132 }
134 } 133 }
diff --git a/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/DropImportAstTransform.java
index 991e91d4..39e599d3 100644
--- a/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/DropImportAstTransform.java
@@ -1,4 +1,4 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.decompiler.languages.java.ast.AstNode; 3import com.strobel.decompiler.languages.java.ast.AstNode;
4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; 4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
diff --git a/src/main/java/cuchaz/enigma/analysis/DropVarModifiersAstTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java
index 0be58911..b8c087b9 100644
--- a/src/main/java/cuchaz/enigma/analysis/DropVarModifiersAstTransform.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java
@@ -1,4 +1,4 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.decompiler.languages.java.ast.*; 3import com.strobel.decompiler.languages.java.ast.*;
4import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 4import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
diff --git a/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java
index 3e052ded..34d95fa5 100644
--- a/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java
@@ -1,4 +1,4 @@
1package oml.ast.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.decompiler.languages.java.ast.AstNode; 3import com.strobel.decompiler.languages.java.ast.AstNode;
4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; 4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
diff --git a/src/main/java/oml/ast/transformers/Java8Generics.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
index 0f8a84c1..8accfc7c 100644
--- a/src/main/java/oml/ast/transformers/Java8Generics.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
@@ -1,4 +1,4 @@
1package oml.ast.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.assembler.metadata.BuiltinTypes; 3import com.strobel.assembler.metadata.BuiltinTypes;
4import com.strobel.assembler.metadata.CommonTypeReferences; 4import com.strobel.assembler.metadata.CommonTypeReferences;
diff --git a/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java
index 6005b7f7..32bb72f4 100644
--- a/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java
@@ -15,7 +15,7 @@
15 * You must not remove this notice, or any other, from this software. 15 * You must not remove this notice, or any other, from this software.
16 */ 16 */
17 17
18package oml.ast.transformers; 18package cuchaz.enigma.source.procyon.transformers;
19 19
20import com.strobel.assembler.metadata.BuiltinTypes; 20import com.strobel.assembler.metadata.BuiltinTypes;
21import com.strobel.assembler.metadata.FieldDefinition; 21import com.strobel.assembler.metadata.FieldDefinition;
diff --git a/src/main/java/oml/ast/transformers/RemoveObjectCasts.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java
index d7c3c4a6..cf0376f3 100644
--- a/src/main/java/oml/ast/transformers/RemoveObjectCasts.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java
@@ -1,4 +1,4 @@
1package oml.ast.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.assembler.metadata.BuiltinTypes; 3import com.strobel.assembler.metadata.BuiltinTypes;
4import com.strobel.decompiler.DecompilerContext; 4import com.strobel.decompiler.DecompilerContext;
diff --git a/src/main/java/oml/ast/transformers/VarargsFixer.java b/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java
index 5810373d..d3ddaab6 100644
--- a/src/main/java/oml/ast/transformers/VarargsFixer.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java
@@ -1,4 +1,4 @@
1package oml.ast.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.assembler.metadata.MemberReference; 3import com.strobel.assembler.metadata.MemberReference;
4import com.strobel.assembler.metadata.MetadataFilters; 4import com.strobel.assembler.metadata.MetadataFilters;
diff --git a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java
index b2aed84d..e702956e 100644
--- a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java
@@ -1,4 +1,4 @@
1package cuchaz.enigma; 1package cuchaz.enigma.source.procyon.typeloader;
2 2
3import com.strobel.assembler.metadata.Buffer; 3import com.strobel.assembler.metadata.Buffer;
4import com.strobel.assembler.metadata.ClasspathTypeLoader; 4import com.strobel.assembler.metadata.ClasspathTypeLoader;
diff --git a/src/main/java/cuchaz/enigma/CachingTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java
index 22c31c63..5be5ddd9 100644
--- a/src/main/java/cuchaz/enigma/CachingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java
@@ -1,4 +1,4 @@
1package cuchaz.enigma; 1package cuchaz.enigma.source.procyon.typeloader;
2 2
3import com.google.common.collect.Maps; 3import com.google.common.collect.Maps;
4import com.strobel.assembler.metadata.Buffer; 4import com.strobel.assembler.metadata.Buffer;
diff --git a/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java
index c746abed..e703d3b3 100644
--- a/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java
@@ -9,11 +9,12 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma.source.procyon.typeloader;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import com.strobel.assembler.metadata.Buffer; 15import com.strobel.assembler.metadata.Buffer;
16import com.strobel.assembler.metadata.ITypeLoader; 16import com.strobel.assembler.metadata.ITypeLoader;
17import cuchaz.enigma.ClassProvider;
17import cuchaz.enigma.translation.representation.entry.ClassEntry; 18import cuchaz.enigma.translation.representation.entry.ClassEntry;
18import org.objectweb.asm.ClassVisitor; 19import org.objectweb.asm.ClassVisitor;
19import org.objectweb.asm.ClassWriter; 20import org.objectweb.asm.ClassWriter;
@@ -32,10 +33,10 @@ public class CompiledSourceTypeLoader extends CachingTypeLoader {
32 //Store one instance as the classpath shouldn't change during load 33 //Store one instance as the classpath shouldn't change during load
33 private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader(); 34 private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader();
34 35
35 private final CompiledSource compiledSource; 36 private final ClassProvider compiledSource;
36 private final LinkedList<Function<ClassVisitor, ClassVisitor>> visitors = new LinkedList<>(); 37 private final LinkedList<Function<ClassVisitor, ClassVisitor>> visitors = new LinkedList<>();
37 38
38 public CompiledSourceTypeLoader(CompiledSource compiledSource) { 39 public CompiledSourceTypeLoader(ClassProvider compiledSource) {
39 this.compiledSource = compiledSource; 40 this.compiledSource = compiledSource;
40 } 41 }
41 42
diff --git a/src/main/java/cuchaz/enigma/NoRetryMetadataSystem.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java
index 269d31e1..c4732b04 100644
--- a/src/main/java/cuchaz/enigma/NoRetryMetadataSystem.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java
@@ -1,4 +1,4 @@
1package cuchaz.enigma; 1package cuchaz.enigma.source.procyon.typeloader;
2 2
3import com.strobel.assembler.metadata.ITypeLoader; 3import com.strobel.assembler.metadata.ITypeLoader;
4import com.strobel.assembler.metadata.MetadataSystem; 4import com.strobel.assembler.metadata.MetadataSystem;
diff --git a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java b/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java
index f6eee690..86c6ecc6 100644
--- a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java
@@ -1,4 +1,4 @@
1package cuchaz.enigma; 1package cuchaz.enigma.source.procyon.typeloader;
2 2
3import com.strobel.assembler.metadata.Buffer; 3import com.strobel.assembler.metadata.Buffer;
4import com.strobel.assembler.metadata.ITypeLoader; 4import com.strobel.assembler.metadata.ITypeLoader;
diff --git a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
index 719d693a..f7ba849e 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
@@ -13,7 +13,6 @@ package cuchaz.enigma.translation.representation;
13 13
14import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import com.strobel.assembler.metadata.TypeReference;
17import cuchaz.enigma.translation.Translatable; 16import cuchaz.enigma.translation.Translatable;
18import cuchaz.enigma.translation.Translator; 17import cuchaz.enigma.translation.Translator;
19import cuchaz.enigma.translation.mapping.EntryMapping; 18import cuchaz.enigma.translation.mapping.EntryMapping;
@@ -112,10 +111,6 @@ public class TypeDescriptor implements Translatable {
112 return new TypeDescriptor("L" + name + ";"); 111 return new TypeDescriptor("L" + name + ";");
113 } 112 }
114 113
115 public static TypeDescriptor parse(TypeReference type) {
116 return new TypeDescriptor(type.getErasedSignature());
117 }
118
119 @Override 114 @Override
120 public String toString() { 115 public String toString() {
121 return this.desc; 116 return this.desc;
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
index 4b245bcb..69307651 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
@@ -12,7 +12,6 @@
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
15import com.strobel.assembler.metadata.TypeDefinition;
16import cuchaz.enigma.translation.Translator; 15import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping; 16import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.representation.AccessFlags; 17import cuchaz.enigma.translation.representation.AccessFlags;
@@ -53,15 +52,6 @@ public class ClassDefEntry extends ClassEntry implements DefEntry<ClassEntry> {
53 return new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access), superClass, interfaceClasses); 52 return new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access), superClass, interfaceClasses);
54 } 53 }
55 54
56 public static ClassDefEntry parse(TypeDefinition def) {
57 String name = def.getInternalName();
58 Signature signature = Signature.createSignature(def.getSignature());
59 AccessFlags access = new AccessFlags(def.getModifiers());
60 ClassEntry superClass = def.getBaseType() != null ? ClassEntry.parse(def.getBaseType()) : null;
61 ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(ClassEntry::parse).toArray(ClassEntry[]::new);
62 return new ClassDefEntry(name, signature, access, superClass, interfaces);
63 }
64
65 public Signature getSignature() { 55 public Signature getSignature() {
66 return signature; 56 return signature;
67 } 57 }
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
index 23ce4a24..74298e43 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
@@ -11,7 +11,6 @@
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import com.strobel.assembler.metadata.TypeReference;
15import cuchaz.enigma.throwables.IllegalNameException; 14import cuchaz.enigma.throwables.IllegalNameException;
16import cuchaz.enigma.translation.Translator; 15import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping; 16import cuchaz.enigma.translation.mapping.EntryMapping;
@@ -47,10 +46,6 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
47 } 46 }
48 } 47 }
49 48
50 public static ClassEntry parse(TypeReference typeReference) {
51 return new ClassEntry(typeReference.getInternalName());
52 }
53
54 @Override 49 @Override
55 public Class<ClassEntry> getParentType() { 50 public Class<ClassEntry> getParentType() {
56 return ClassEntry.class; 51 return ClassEntry.class;
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
index 46c0b003..f9282b28 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
@@ -12,7 +12,6 @@
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
15import com.strobel.assembler.metadata.FieldDefinition;
16import cuchaz.enigma.translation.Translator; 15import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping; 16import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.representation.AccessFlags; 17import cuchaz.enigma.translation.representation.AccessFlags;
@@ -41,14 +40,6 @@ public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> {
41 return new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access), null); 40 return new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access), null);
42 } 41 }
43 42
44 public static FieldDefEntry parse(FieldDefinition definition) {
45 ClassEntry owner = ClassEntry.parse(definition.getDeclaringType());
46 TypeDescriptor descriptor = new TypeDescriptor(definition.getErasedSignature());
47 Signature signature = Signature.createTypedSignature(definition.getSignature());
48 AccessFlags access = new AccessFlags(definition.getModifiers());
49 return new FieldDefEntry(owner, definition.getName(), descriptor, signature, access, null);
50 }
51
52 @Override 43 @Override
53 public AccessFlags getAccess() { 44 public AccessFlags getAccess() {
54 return access; 45 return access;
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
index 280b605d..4e75a5c8 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
@@ -12,7 +12,6 @@
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
15import com.strobel.assembler.metadata.MethodDefinition;
16import cuchaz.enigma.translation.Translator; 15import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping; 16import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.representation.AccessFlags; 17import cuchaz.enigma.translation.representation.AccessFlags;
@@ -41,14 +40,6 @@ public class MethodDefEntry extends MethodEntry implements DefEntry<ClassEntry>
41 return new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access), null); 40 return new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access), null);
42 } 41 }
43 42
44 public static MethodDefEntry parse(MethodDefinition definition) {
45 ClassEntry classEntry = ClassEntry.parse(definition.getDeclaringType());
46 MethodDescriptor descriptor = new MethodDescriptor(definition.getErasedSignature());
47 Signature signature = Signature.createSignature(definition.getSignature());
48 AccessFlags access = new AccessFlags(definition.getModifiers());
49 return new MethodDefEntry(classEntry, definition.getName(), descriptor, signature, access, null);
50 }
51
52 @Override 43 @Override
53 public AccessFlags getAccess() { 44 public AccessFlags getAccess() {
54 return access; 45 return access;
diff --git a/src/main/java/cuchaz/enigma/utils/Pair.java b/src/main/java/cuchaz/enigma/utils/Pair.java
new file mode 100644
index 00000000..bf02ceff
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/utils/Pair.java
@@ -0,0 +1,26 @@
1package cuchaz.enigma.utils;
2
3import java.util.Objects;
4
5public class Pair<A, B> {
6 public final A a;
7 public final B b;
8
9 public Pair(A a, B b) {
10 this.a = a;
11 this.b = b;
12 }
13
14 @Override
15 public int hashCode() {
16 return Objects.hashCode(a) * 31 +
17 Objects.hashCode(b);
18 }
19
20 @Override
21 public boolean equals(Object o) {
22 return o instanceof Pair &&
23 Objects.equals(a, ((Pair<?, ?>) o).a) &&
24 Objects.equals(b, ((Pair<?, ?>) o).b);
25 }
26}
diff --git a/src/main/java/oml/ExtraClasspathTypeLoader.java b/src/main/java/oml/ExtraClasspathTypeLoader.java
deleted file mode 100644
index f8ec2e0c..00000000
--- a/src/main/java/oml/ExtraClasspathTypeLoader.java
+++ /dev/null
@@ -1,59 +0,0 @@
1package oml;
2
3import com.strobel.assembler.metadata.Buffer;
4import com.strobel.assembler.metadata.ITypeLoader;
5
6import java.io.File;
7import java.io.IOException;
8import java.io.InputStream;
9import java.net.MalformedURLException;
10import java.net.URL;
11import java.net.URLClassLoader;
12import java.util.Arrays;
13
14/**
15 * Copy of ClasspathTypeLoader supporting a classpath constructor.
16 */
17public class ExtraClasspathTypeLoader implements ITypeLoader {
18 private final ClassLoader _loader;
19
20 public ExtraClasspathTypeLoader(String extraClasspath){
21 _loader = new URLClassLoader(Arrays.stream(extraClasspath.split(File.pathSeparator)).map(path-> {
22 try {
23 return new File(path).toURI().toURL();
24 } catch (MalformedURLException e) {
25 throw new RuntimeException(e);
26 }
27 }).toArray(URL[]::new));
28 }
29
30 @Override
31 public boolean tryLoadType(final String internalName, final Buffer buffer) {
32
33 final String path = internalName.concat(".class");
34 final URL resource = _loader.getResource(path);
35
36 if (resource == null) {
37 return false;
38 }
39
40 try (final InputStream stream = _loader.getResourceAsStream(path)) {
41 final byte[] temp = new byte[4096];
42
43 int bytesRead;
44
45 while ((bytesRead = stream.read(temp, 0, temp.length)) > 0) {
46 //buffer.ensureWriteableBytes(bytesRead);
47 buffer.putByteArray(temp, 0, bytesRead);
48 }
49
50 buffer.flip();
51
52
53 return true;
54 }
55 catch (final IOException ignored) {
56 return false;
57 }
58 }
59}
diff --git a/src/main/resources/lang/en_us.json b/src/main/resources/lang/en_us.json
index dbf3e451..8cd5823f 100644
--- a/src/main/resources/lang/en_us.json
+++ b/src/main/resources/lang/en_us.json
@@ -26,6 +26,7 @@
26 "menu.file.stats.title": "Choose Included Members", 26 "menu.file.stats.title": "Choose Included Members",
27 "menu.file.stats.generate": "Generate Stats", 27 "menu.file.stats.generate": "Generate Stats",
28 "menu.file.exit": "Exit", 28 "menu.file.exit": "Exit",
29 "menu.decompiler": "Decompiler",
29 "menu.view": "View", 30 "menu.view": "View",
30 "menu.view.themes": "Themes", 31 "menu.view.themes": "Themes",
31 "menu.view.themes.default": "Default", 32 "menu.view.themes.default": "Default",
diff --git a/src/main/resources/lang/fr_fr.json b/src/main/resources/lang/fr_fr.json
index ceac796a..5c4c514f 100644
--- a/src/main/resources/lang/fr_fr.json
+++ b/src/main/resources/lang/fr_fr.json
@@ -26,6 +26,7 @@
26 "menu.file.stats.title": "Choisir les membres inclus", 26 "menu.file.stats.title": "Choisir les membres inclus",
27 "menu.file.stats.generate": "Générer les statistiques", 27 "menu.file.stats.generate": "Générer les statistiques",
28 "menu.file.exit": "Quitter", 28 "menu.file.exit": "Quitter",
29 "menu.decompiler": "Décompilateur",
29 "menu.view": "Affichage", 30 "menu.view": "Affichage",
30 "menu.view.themes": "Thèmes", 31 "menu.view.themes": "Thèmes",
31 "menu.view.themes.default": "Par défaut", 32 "menu.view.themes.default": "Par défaut",