diff options
| author | 2020-03-09 06:04:08 -0400 | |
|---|---|---|
| committer | 2020-03-09 10:04:08 +0000 | |
| commit | 58c0aeb15a65324de08a914dfa62cc68a516a4e3 (patch) | |
| tree | f45e8141c0864692051149a478c5a0a6bbe68686 /src | |
| parent | Made Enigma gui translatable (#193) (diff) | |
| download | enigma-58c0aeb15a65324de08a914dfa62cc68a516a4e3.tar.gz enigma-58c0aeb15a65324de08a914dfa62cc68a516a4e3.tar.xz enigma-58c0aeb15a65324de08a914dfa62cc68a516a4e3.zip | |
CFR support (#192)
* Add decompiler API
* Add CFR support
Diffstat (limited to 'src')
57 files changed, 1302 insertions, 750 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 | ||
| 5 | import javax.annotation.Nullable; | 5 | import javax.annotation.Nullable; |
| 6 | 6 | ||
| 7 | public interface CompiledSource { | 7 | public 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma; |
| 2 | 2 | ||
| 3 | import com.google.common.base.Functions; | 3 | import com.google.common.base.Functions; |
| 4 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 5 | import com.strobel.assembler.metadata.MetadataSystem; | ||
| 6 | import com.strobel.decompiler.DecompilerSettings; | ||
| 7 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 8 | import cuchaz.enigma.analysis.ClassCache; | 4 | import cuchaz.enigma.analysis.ClassCache; |
| 9 | import cuchaz.enigma.analysis.EntryReference; | 5 | import cuchaz.enigma.analysis.EntryReference; |
| 10 | import cuchaz.enigma.analysis.index.JarIndex; | 6 | import cuchaz.enigma.analysis.index.JarIndex; |
| 11 | import cuchaz.enigma.api.service.NameProposalService; | 7 | import cuchaz.enigma.api.service.NameProposalService; |
| 12 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; | 8 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; |
| 13 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; | 9 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; |
| 10 | import cuchaz.enigma.source.*; | ||
| 14 | import cuchaz.enigma.translation.Translator; | 11 | import cuchaz.enigma.translation.Translator; |
| 15 | import cuchaz.enigma.translation.mapping.*; | 12 | import cuchaz.enigma.translation.mapping.*; |
| 16 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | 13 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; |
| @@ -25,10 +22,7 @@ import org.objectweb.asm.ClassWriter; | |||
| 25 | import org.objectweb.asm.Opcodes; | 22 | import org.objectweb.asm.Opcodes; |
| 26 | import org.objectweb.asm.tree.ClassNode; | 23 | import org.objectweb.asm.tree.ClassNode; |
| 27 | 24 | ||
| 28 | import java.io.BufferedWriter; | 25 | import java.io.*; |
| 29 | import java.io.IOException; | ||
| 30 | import java.io.PrintWriter; | ||
| 31 | import java.io.StringWriter; | ||
| 32 | import java.nio.file.Files; | 26 | import java.nio.file.Files; |
| 33 | import java.nio.file.Path; | 27 | import java.nio.file.Path; |
| 34 | import java.util.Collection; | 28 | import 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 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 4 | import com.strobel.assembler.metadata.MetadataSystem; | ||
| 5 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 6 | import com.strobel.assembler.metadata.TypeReference; | ||
| 7 | import com.strobel.decompiler.DecompilerContext; | ||
| 8 | import com.strobel.decompiler.DecompilerSettings; | ||
| 9 | import com.strobel.decompiler.PlainTextOutput; | ||
| 10 | import com.strobel.decompiler.languages.java.BraceStyle; | ||
| 11 | import com.strobel.decompiler.languages.java.JavaFormattingOptions; | ||
| 12 | import com.strobel.decompiler.languages.java.JavaOutputVisitor; | ||
| 13 | import com.strobel.decompiler.languages.java.ast.AstBuilder; | ||
| 14 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 15 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; | ||
| 16 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 17 | import cuchaz.enigma.utils.Utils; | ||
| 18 | import oml.ast.transformers.*; | ||
| 19 | |||
| 20 | import java.io.StringWriter; | ||
| 21 | import java.io.Writer; | ||
| 22 | import java.lang.ref.WeakReference; | ||
| 23 | import java.util.Arrays; | ||
| 24 | import java.util.List; | ||
| 25 | import java.util.Objects; | ||
| 26 | |||
| 27 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | 1 | package cuchaz.enigma.analysis; |
| 2 | 2 | ||
| 3 | import com.strobel.core.Pair; | ||
| 4 | import cuchaz.enigma.api.EnigmaPlugin; | 3 | import cuchaz.enigma.api.EnigmaPlugin; |
| 5 | import cuchaz.enigma.api.EnigmaPluginContext; | 4 | import cuchaz.enigma.api.EnigmaPluginContext; |
| 6 | import cuchaz.enigma.api.service.JarIndexerService; | 5 | import cuchaz.enigma.api.service.JarIndexerService; |
| 7 | import cuchaz.enigma.api.service.NameProposalService; | 6 | import cuchaz.enigma.api.service.NameProposalService; |
| 8 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | 7 | import cuchaz.enigma.source.DecompilerService; |
| 8 | import cuchaz.enigma.source.Decompilers; | ||
| 9 | import cuchaz.enigma.source.procyon.ProcyonDecompiler; | ||
| 9 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 10 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 10 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 11 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 11 | import cuchaz.enigma.translation.representation.entry.Entry; | 12 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 12 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | 13 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 13 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 14 | import cuchaz.enigma.utils.Pair; |
| 14 | import org.objectweb.asm.ClassReader; | 15 | import org.objectweb.asm.ClassReader; |
| 15 | import org.objectweb.asm.ClassVisitor; | 16 | import org.objectweb.asm.ClassVisitor; |
| 16 | import org.objectweb.asm.FieldVisitor; | 17 | import org.objectweb.asm.FieldVisitor; |
| @@ -34,7 +35,6 @@ import java.util.List; | |||
| 34 | import java.util.Map; | 35 | import java.util.Map; |
| 35 | import java.util.Optional; | 36 | import java.util.Optional; |
| 36 | import java.util.Set; | 37 | import java.util.Set; |
| 37 | import java.util.function.UnaryOperator; | ||
| 38 | 38 | ||
| 39 | public final class BuiltinPlugin implements EnigmaPlugin { | 39 | public 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; | |||
| 3 | import com.google.common.cache.Cache; | 3 | import com.google.common.cache.Cache; |
| 4 | import com.google.common.cache.CacheBuilder; | 4 | import com.google.common.cache.CacheBuilder; |
| 5 | import com.google.common.collect.ImmutableSet; | 5 | import com.google.common.collect.ImmutableSet; |
| 6 | import cuchaz.enigma.CompiledSource; | 6 | import cuchaz.enigma.ClassProvider; |
| 7 | import cuchaz.enigma.ProgressListener; | 7 | import cuchaz.enigma.ProgressListener; |
| 8 | import cuchaz.enigma.analysis.index.JarIndex; | 8 | import cuchaz.enigma.analysis.index.JarIndex; |
| 9 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; | 9 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; |
| @@ -22,7 +22,7 @@ import java.util.concurrent.ExecutionException; | |||
| 22 | import java.util.concurrent.TimeUnit; | 22 | import java.util.concurrent.TimeUnit; |
| 23 | import java.util.function.Supplier; | 23 | import java.util.function.Supplier; |
| 24 | 24 | ||
| 25 | public final class ClassCache implements AutoCloseable, CompiledSource { | 25 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.google.common.collect.Maps; | ||
| 17 | import com.google.common.collect.Multimap; | ||
| 18 | import com.strobel.decompiler.languages.Region; | ||
| 19 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 20 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 21 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; | ||
| 22 | import com.strobel.decompiler.languages.java.ast.Identifier; | ||
| 23 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | ||
| 24 | import cuchaz.enigma.gui.SourceRemapper; | ||
| 25 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 26 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 27 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 28 | |||
| 29 | import javax.annotation.Nullable; | ||
| 30 | import java.util.Collection; | ||
| 31 | import java.util.List; | ||
| 32 | import java.util.Map; | ||
| 33 | import java.util.TreeMap; | ||
| 34 | import java.util.regex.Pattern; | ||
| 35 | import java.util.stream.Collectors; | ||
| 36 | |||
| 37 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.strobel.componentmodel.Key; | ||
| 15 | import com.strobel.decompiler.languages.java.ast.*; | ||
| 16 | import com.strobel.decompiler.patterns.Pattern; | ||
| 17 | |||
| 18 | import java.io.*; | ||
| 19 | import java.nio.charset.Charset; | ||
| 20 | |||
| 21 | public 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 | ||
| 3 | import cuchaz.enigma.EnigmaProject; | 3 | import cuchaz.enigma.EnigmaProject; |
| 4 | import cuchaz.enigma.ProgressListener; | 4 | import cuchaz.enigma.ProgressListener; |
| 5 | import cuchaz.enigma.source.DecompilerService; | ||
| 6 | import cuchaz.enigma.source.Decompilers; | ||
| 5 | 7 | ||
| 8 | import java.lang.reflect.Field; | ||
| 6 | import java.nio.file.Path; | 9 | import java.nio.file.Path; |
| 10 | import java.util.Locale; | ||
| 7 | 11 | ||
| 8 | public class DecompileCommand extends Command { | 12 | public 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; | |||
| 3 | import com.bulenkov.darcula.DarculaLaf; | 3 | import com.bulenkov.darcula.DarculaLaf; |
| 4 | import com.google.common.io.Files; | 4 | import com.google.common.io.Files; |
| 5 | import com.google.gson.*; | 5 | import com.google.gson.*; |
| 6 | import cuchaz.enigma.source.DecompilerService; | ||
| 7 | import cuchaz.enigma.source.Decompilers; | ||
| 6 | 8 | ||
| 7 | import cuchaz.enigma.utils.I18n; | 9 | import 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; | |||
| 3 | import cuchaz.enigma.EnigmaProject; | 3 | import cuchaz.enigma.EnigmaProject; |
| 4 | import cuchaz.enigma.EnigmaServices; | 4 | import cuchaz.enigma.EnigmaServices; |
| 5 | import cuchaz.enigma.analysis.EntryReference; | 5 | import cuchaz.enigma.analysis.EntryReference; |
| 6 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 7 | import cuchaz.enigma.analysis.Token; | 6 | import cuchaz.enigma.analysis.Token; |
| 8 | import cuchaz.enigma.api.service.NameProposalService; | 7 | import cuchaz.enigma.api.service.NameProposalService; |
| 9 | import cuchaz.enigma.gui.highlight.TokenHighlightType; | 8 | import cuchaz.enigma.gui.highlight.TokenHighlightType; |
| 9 | import cuchaz.enigma.source.Decompiler; | ||
| 10 | import cuchaz.enigma.source.SourceIndex; | ||
| 10 | import cuchaz.enigma.translation.LocalNameGenerator; | 11 | import cuchaz.enigma.translation.LocalNameGenerator; |
| 11 | import cuchaz.enigma.translation.Translator; | 12 | import cuchaz.enigma.translation.Translator; |
| 12 | import cuchaz.enigma.translation.mapping.EntryRemapper; | 13 | import cuchaz.enigma.translation.mapping.EntryRemapper; |
| 13 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 14 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | 14 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; |
| 15 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 15 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| @@ -19,7 +19,6 @@ import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry; | |||
| 19 | 19 | ||
| 20 | import javax.annotation.Nullable; | 20 | import javax.annotation.Nullable; |
| 21 | import java.util.*; | 21 | import java.util.*; |
| 22 | import java.util.stream.Stream; | ||
| 23 | 22 | ||
| 24 | public class DecompiledClassSource { | 23 | public 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 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import com.google.common.util.concurrent.ThreadFactoryBuilder; | 15 | import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| 16 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 16 | import cuchaz.enigma.Enigma; |
| 17 | import cuchaz.enigma.*; | 17 | import cuchaz.enigma.EnigmaProfile; |
| 18 | import cuchaz.enigma.EnigmaProject; | ||
| 18 | import cuchaz.enigma.analysis.*; | 19 | import cuchaz.enigma.analysis.*; |
| 19 | import cuchaz.enigma.api.service.ObfuscationTestService; | 20 | import cuchaz.enigma.api.service.ObfuscationTestService; |
| 20 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; | 21 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; |
| @@ -23,6 +24,7 @@ import cuchaz.enigma.gui.dialog.ProgressDialog; | |||
| 23 | import cuchaz.enigma.gui.stats.StatsGenerator; | 24 | import cuchaz.enigma.gui.stats.StatsGenerator; |
| 24 | import cuchaz.enigma.gui.stats.StatsMember; | 25 | import cuchaz.enigma.gui.stats.StatsMember; |
| 25 | import cuchaz.enigma.gui.util.History; | 26 | import cuchaz.enigma.gui.util.History; |
| 27 | import cuchaz.enigma.source.*; | ||
| 26 | import cuchaz.enigma.throwables.MappingParseException; | 28 | import cuchaz.enigma.throwables.MappingParseException; |
| 27 | import cuchaz.enigma.translation.Translator; | 29 | import cuchaz.enigma.translation.Translator; |
| 28 | import cuchaz.enigma.translation.mapping.*; | 30 | import cuchaz.enigma.translation.mapping.*; |
| @@ -36,16 +38,16 @@ import cuchaz.enigma.utils.I18n; | |||
| 36 | import cuchaz.enigma.utils.ReadableToken; | 38 | import cuchaz.enigma.utils.ReadableToken; |
| 37 | import cuchaz.enigma.utils.Utils; | 39 | import cuchaz.enigma.utils.Utils; |
| 38 | import org.objectweb.asm.Opcodes; | 40 | import org.objectweb.asm.Opcodes; |
| 41 | import org.objectweb.asm.tree.ClassNode; | ||
| 39 | 42 | ||
| 40 | import javax.annotation.Nullable; | 43 | import javax.annotation.Nullable; |
| 41 | import javax.swing.*; | 44 | import javax.swing.JOptionPane; |
| 42 | import java.awt.*; | 45 | import java.awt.Desktop; |
| 43 | import java.awt.event.ItemEvent; | 46 | import java.awt.event.ItemEvent; |
| 44 | import java.io.*; | 47 | import java.io.*; |
| 45 | import java.nio.file.Path; | 48 | import java.nio.file.Path; |
| 46 | import java.util.Collection; | 49 | import java.util.Collection; |
| 47 | import java.util.List; | 50 | import java.util.List; |
| 48 | import java.util.Optional; | ||
| 49 | import java.util.Set; | 51 | import java.util.Set; |
| 50 | import java.util.concurrent.CompletableFuture; | 52 | import java.util.concurrent.CompletableFuture; |
| 51 | import java.util.concurrent.ExecutorService; | 53 | import 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 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | public 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; | |||
| 8 | import cuchaz.enigma.gui.stats.StatsMember; | 8 | import cuchaz.enigma.gui.stats.StatsMember; |
| 9 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | 9 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; |
| 10 | import cuchaz.enigma.utils.I18n; | 10 | import cuchaz.enigma.utils.I18n; |
| 11 | import cuchaz.enigma.utils.Utils; | ||
| 11 | 12 | ||
| 12 | import javax.swing.*; | 13 | import javax.swing.*; |
| 13 | import java.awt.*; | 14 | import 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 @@ | |||
| 1 | package cuchaz.enigma.source; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ClassProvider; | ||
| 4 | import cuchaz.enigma.api.service.EnigmaService; | ||
| 5 | import cuchaz.enigma.api.service.EnigmaServiceType; | ||
| 6 | |||
| 7 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source; | ||
| 2 | |||
| 3 | import cuchaz.enigma.source.cfr.CfrDecompiler; | ||
| 4 | import cuchaz.enigma.source.procyon.ProcyonDecompiler; | ||
| 5 | |||
| 6 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source; | ||
| 2 | |||
| 3 | import com.google.common.collect.HashMultimap; | ||
| 4 | import com.google.common.collect.Lists; | ||
| 5 | import com.google.common.collect.Maps; | ||
| 6 | import com.google.common.collect.Multimap; | ||
| 7 | import cuchaz.enigma.analysis.EntryReference; | ||
| 8 | import cuchaz.enigma.analysis.Token; | ||
| 9 | import cuchaz.enigma.gui.SourceRemapper; | ||
| 10 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 11 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 13 | |||
| 14 | import java.util.Collection; | ||
| 15 | import java.util.List; | ||
| 16 | import java.util.Map; | ||
| 17 | import java.util.TreeMap; | ||
| 18 | import java.util.stream.Collectors; | ||
| 19 | |||
| 20 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | ||
| 2 | |||
| 3 | import com.google.common.io.ByteStreams; | ||
| 4 | import cuchaz.enigma.ClassProvider; | ||
| 5 | import cuchaz.enigma.source.Decompiler; | ||
| 6 | import cuchaz.enigma.source.Source; | ||
| 7 | import cuchaz.enigma.source.SourceSettings; | ||
| 8 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; | ||
| 9 | import org.benf.cfr.reader.apiunreleased.JarContent; | ||
| 10 | import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; | ||
| 11 | import org.benf.cfr.reader.entities.ClassFile; | ||
| 12 | import org.benf.cfr.reader.mapping.MappingFactory; | ||
| 13 | import org.benf.cfr.reader.mapping.ObfuscationMapping; | ||
| 14 | import org.benf.cfr.reader.relationship.MemberNameResolver; | ||
| 15 | import org.benf.cfr.reader.state.DCCommonState; | ||
| 16 | import org.benf.cfr.reader.state.TypeUsageCollectingDumper; | ||
| 17 | import org.benf.cfr.reader.util.AnalysisType; | ||
| 18 | import org.benf.cfr.reader.util.CannotLoadClassException; | ||
| 19 | import org.benf.cfr.reader.util.collections.ListFactory; | ||
| 20 | import org.benf.cfr.reader.util.getopt.Options; | ||
| 21 | import org.benf.cfr.reader.util.getopt.OptionsImpl; | ||
| 22 | import org.objectweb.asm.ClassWriter; | ||
| 23 | import org.objectweb.asm.tree.ClassNode; | ||
| 24 | |||
| 25 | import java.io.IOException; | ||
| 26 | import java.io.InputStream; | ||
| 27 | import java.util.Collection; | ||
| 28 | import java.util.HashMap; | ||
| 29 | import java.util.Map; | ||
| 30 | |||
| 31 | |||
| 32 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | ||
| 2 | |||
| 3 | import cuchaz.enigma.source.Source; | ||
| 4 | import cuchaz.enigma.source.SourceIndex; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 6 | import org.benf.cfr.reader.entities.ClassFile; | ||
| 7 | import org.benf.cfr.reader.state.DCCommonState; | ||
| 8 | import org.benf.cfr.reader.state.TypeUsageInformation; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.Token; | ||
| 4 | import cuchaz.enigma.source.SourceIndex; | ||
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 6 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 8 | import org.benf.cfr.reader.bytecode.analysis.types.*; | ||
| 9 | import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable; | ||
| 10 | import org.benf.cfr.reader.entities.Field; | ||
| 11 | import org.benf.cfr.reader.entities.Method; | ||
| 12 | import org.benf.cfr.reader.mapping.NullMapping; | ||
| 13 | import org.benf.cfr.reader.mapping.ObfuscationMapping; | ||
| 14 | import org.benf.cfr.reader.state.TypeUsageInformation; | ||
| 15 | import org.benf.cfr.reader.util.collections.SetFactory; | ||
| 16 | import org.benf.cfr.reader.util.output.DelegatingDumper; | ||
| 17 | import org.benf.cfr.reader.util.output.Dumpable; | ||
| 18 | import org.benf.cfr.reader.util.output.Dumper; | ||
| 19 | import org.benf.cfr.reader.util.output.TypeContext; | ||
| 20 | |||
| 21 | import java.util.Set; | ||
| 22 | import java.util.stream.Collectors; | ||
| 23 | |||
| 24 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.procyon; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.FieldDefinition; | ||
| 4 | import com.strobel.assembler.metadata.MethodDefinition; | ||
| 5 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 6 | import com.strobel.assembler.metadata.TypeReference; | ||
| 7 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 8 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 9 | import cuchaz.enigma.translation.representation.Signature; | ||
| 10 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 15 | |||
| 16 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.procyon; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 4 | import com.strobel.assembler.metadata.MetadataSystem; | ||
| 5 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 6 | import com.strobel.assembler.metadata.TypeReference; | ||
| 7 | import com.strobel.decompiler.DecompilerContext; | ||
| 8 | import com.strobel.decompiler.DecompilerSettings; | ||
| 9 | import com.strobel.decompiler.languages.java.BraceStyle; | ||
| 10 | import com.strobel.decompiler.languages.java.JavaFormattingOptions; | ||
| 11 | import com.strobel.decompiler.languages.java.ast.AstBuilder; | ||
| 12 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 13 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; | ||
| 14 | import cuchaz.enigma.ClassProvider; | ||
| 15 | import cuchaz.enigma.api.EnigmaPluginContext; | ||
| 16 | import cuchaz.enigma.source.Source; | ||
| 17 | import cuchaz.enigma.source.Decompiler; | ||
| 18 | import cuchaz.enigma.source.SourceSettings; | ||
| 19 | import cuchaz.enigma.source.procyon.transformers.*; | ||
| 20 | import cuchaz.enigma.source.procyon.typeloader.CompiledSourceTypeLoader; | ||
| 21 | import cuchaz.enigma.source.procyon.typeloader.NoRetryMetadataSystem; | ||
| 22 | import cuchaz.enigma.source.procyon.typeloader.SynchronizedTypeLoader; | ||
| 23 | import cuchaz.enigma.utils.Utils; | ||
| 24 | |||
| 25 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.procyon; | ||
| 2 | |||
| 3 | import com.strobel.decompiler.DecompilerSettings; | ||
| 4 | import com.strobel.decompiler.PlainTextOutput; | ||
| 5 | import com.strobel.decompiler.languages.java.JavaOutputVisitor; | ||
| 6 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 7 | import cuchaz.enigma.source.Source; | ||
| 8 | import cuchaz.enigma.source.SourceIndex; | ||
| 9 | import cuchaz.enigma.source.procyon.index.SourceIndexVisitor; | ||
| 10 | import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform; | ||
| 11 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 12 | |||
| 13 | import java.io.StringWriter; | ||
| 14 | |||
| 15 | public 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.source.procyon.index; |
| 13 | 13 | ||
| 14 | import com.strobel.assembler.metadata.FieldDefinition; | 14 | import com.strobel.assembler.metadata.FieldDefinition; |
| 15 | import com.strobel.assembler.metadata.MethodDefinition; | 15 | import com.strobel.assembler.metadata.MethodDefinition; |
| @@ -17,10 +17,9 @@ import com.strobel.assembler.metadata.TypeDefinition; | |||
| 17 | import com.strobel.assembler.metadata.TypeReference; | 17 | import com.strobel.assembler.metadata.TypeReference; |
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | 20 | import cuchaz.enigma.source.SourceIndex; |
| 21 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 21 | import cuchaz.enigma.source.procyon.EntryParser; |
| 22 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | 22 | import cuchaz.enigma.translation.representation.entry.*; |
| 23 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 24 | 23 | ||
| 25 | public class SourceIndexClassVisitor extends SourceIndexVisitor { | 24 | public 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.source.procyon.index; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 15 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| @@ -17,6 +17,8 @@ import com.strobel.assembler.metadata.*; | |||
| 17 | import com.strobel.decompiler.ast.Variable; | 17 | import com.strobel.decompiler.ast.Variable; |
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.source.SourceIndex; | ||
| 21 | import cuchaz.enigma.source.procyon.EntryParser; | ||
| 20 | import cuchaz.enigma.translation.representation.MethodDescriptor; | 22 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| 21 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 23 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 22 | import cuchaz.enigma.translation.representation.entry.*; | 24 | import 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.source.procyon.index; |
| 13 | 13 | ||
| 14 | import com.strobel.assembler.metadata.TypeDefinition; | 14 | import com.strobel.assembler.metadata.TypeDefinition; |
| 15 | import com.strobel.decompiler.languages.java.ast.AstNode; | 15 | import com.strobel.decompiler.languages.java.ast.AstNode; |
| 16 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | 16 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; |
| 17 | import com.strobel.decompiler.languages.java.ast.Keys; | 17 | import com.strobel.decompiler.languages.java.ast.Keys; |
| 18 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | 18 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; |
| 19 | import cuchaz.enigma.source.SourceIndex; | ||
| 20 | import cuchaz.enigma.source.procyon.EntryParser; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | 21 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 20 | 22 | ||
| 21 | public class SourceIndexVisitor extends DepthFirstAstVisitor<SourceIndex, Void> { | 23 | public 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 @@ | |||
| 1 | package cuchaz.enigma.source.procyon.index; | ||
| 2 | |||
| 3 | import com.strobel.decompiler.languages.Region; | ||
| 4 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 5 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; | ||
| 6 | import com.strobel.decompiler.languages.java.ast.Identifier; | ||
| 7 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | ||
| 8 | import cuchaz.enigma.analysis.Token; | ||
| 9 | import cuchaz.enigma.source.SourceIndex; | ||
| 10 | |||
| 11 | import java.util.regex.Pattern; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | 1 | package cuchaz.enigma.source.procyon.transformers; |
| 2 | 2 | ||
| 3 | import com.google.common.base.Function; | 3 | import com.google.common.base.Function; |
| 4 | import com.google.common.base.Strings; | 4 | import com.google.common.base.Strings; |
| 5 | import com.strobel.assembler.metadata.ParameterDefinition; | 5 | import com.strobel.assembler.metadata.ParameterDefinition; |
| 6 | import com.strobel.decompiler.languages.java.ast.*; | 6 | import com.strobel.decompiler.languages.java.ast.*; |
| 7 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | 7 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; |
| 8 | import cuchaz.enigma.source.procyon.EntryParser; | ||
| 8 | import cuchaz.enigma.translation.mapping.EntryMapping; | 9 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 9 | import cuchaz.enigma.translation.mapping.EntryRemapper; | 10 | import cuchaz.enigma.translation.mapping.EntryRemapper; |
| 10 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 11 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | 11 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; |
| 12 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.*; | 12 | import cuchaz.enigma.translation.representation.entry.*; |
| 14 | 13 | ||
| 15 | import java.util.ArrayList; | 14 | import 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | 1 | package cuchaz.enigma.source.procyon.transformers; |
| 2 | 2 | ||
| 3 | import com.strobel.decompiler.languages.java.ast.AstNode; | 3 | import com.strobel.decompiler.languages.java.ast.AstNode; |
| 4 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | 4 | import 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | 1 | package cuchaz.enigma.source.procyon.transformers; |
| 2 | 2 | ||
| 3 | import com.strobel.decompiler.languages.java.ast.*; | 3 | import com.strobel.decompiler.languages.java.ast.*; |
| 4 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | 4 | import 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 @@ | |||
| 1 | package oml.ast.transformers; | 1 | package cuchaz.enigma.source.procyon.transformers; |
| 2 | 2 | ||
| 3 | import com.strobel.decompiler.languages.java.ast.AstNode; | 3 | import com.strobel.decompiler.languages.java.ast.AstNode; |
| 4 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | 4 | import 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 @@ | |||
| 1 | package oml.ast.transformers; | 1 | package cuchaz.enigma.source.procyon.transformers; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.BuiltinTypes; | 3 | import com.strobel.assembler.metadata.BuiltinTypes; |
| 4 | import com.strobel.assembler.metadata.CommonTypeReferences; | 4 | import 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 | ||
| 18 | package oml.ast.transformers; | 18 | package cuchaz.enigma.source.procyon.transformers; |
| 19 | 19 | ||
| 20 | import com.strobel.assembler.metadata.BuiltinTypes; | 20 | import com.strobel.assembler.metadata.BuiltinTypes; |
| 21 | import com.strobel.assembler.metadata.FieldDefinition; | 21 | import 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 @@ | |||
| 1 | package oml.ast.transformers; | 1 | package cuchaz.enigma.source.procyon.transformers; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.BuiltinTypes; | 3 | import com.strobel.assembler.metadata.BuiltinTypes; |
| 4 | import com.strobel.decompiler.DecompilerContext; | 4 | import 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 @@ | |||
| 1 | package oml.ast.transformers; | 1 | package cuchaz.enigma.source.procyon.transformers; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.MemberReference; | 3 | import com.strobel.assembler.metadata.MemberReference; |
| 4 | import com.strobel.assembler.metadata.MetadataFilters; | 4 | import 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma.source.procyon.typeloader; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.Buffer; | 3 | import com.strobel.assembler.metadata.Buffer; |
| 4 | import com.strobel.assembler.metadata.ClasspathTypeLoader; | 4 | import 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma.source.procyon.typeloader; |
| 2 | 2 | ||
| 3 | import com.google.common.collect.Maps; | 3 | import com.google.common.collect.Maps; |
| 4 | import com.strobel.assembler.metadata.Buffer; | 4 | import 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma.source.procyon.typeloader; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import com.strobel.assembler.metadata.Buffer; | 15 | import com.strobel.assembler.metadata.Buffer; |
| 16 | import com.strobel.assembler.metadata.ITypeLoader; | 16 | import com.strobel.assembler.metadata.ITypeLoader; |
| 17 | import cuchaz.enigma.ClassProvider; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 18 | import org.objectweb.asm.ClassVisitor; | 19 | import org.objectweb.asm.ClassVisitor; |
| 19 | import org.objectweb.asm.ClassWriter; | 20 | import 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma.source.procyon.typeloader; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.ITypeLoader; | 3 | import com.strobel.assembler.metadata.ITypeLoader; |
| 4 | import com.strobel.assembler.metadata.MetadataSystem; | 4 | import 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma.source.procyon.typeloader; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.Buffer; | 3 | import com.strobel.assembler.metadata.Buffer; |
| 4 | import com.strobel.assembler.metadata.ITypeLoader; | 4 | import 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 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 16 | import com.strobel.assembler.metadata.TypeReference; | ||
| 17 | import cuchaz.enigma.translation.Translatable; | 16 | import cuchaz.enigma.translation.Translatable; |
| 18 | import cuchaz.enigma.translation.Translator; | 17 | import cuchaz.enigma.translation.Translator; |
| 19 | import cuchaz.enigma.translation.mapping.EntryMapping; | 18 | import 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 @@ | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | 12 | package cuchaz.enigma.translation.representation.entry; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 16 | import cuchaz.enigma.translation.Translator; | 15 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | 16 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 18 | import cuchaz.enigma.translation.representation.AccessFlags; | 17 | import 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 | ||
| 12 | package cuchaz.enigma.translation.representation.entry; | 12 | package cuchaz.enigma.translation.representation.entry; |
| 13 | 13 | ||
| 14 | import com.strobel.assembler.metadata.TypeReference; | ||
| 15 | import cuchaz.enigma.throwables.IllegalNameException; | 14 | import cuchaz.enigma.throwables.IllegalNameException; |
| 16 | import cuchaz.enigma.translation.Translator; | 15 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | 16 | import 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 @@ | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | 12 | package cuchaz.enigma.translation.representation.entry; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.strobel.assembler.metadata.FieldDefinition; | ||
| 16 | import cuchaz.enigma.translation.Translator; | 15 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | 16 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 18 | import cuchaz.enigma.translation.representation.AccessFlags; | 17 | import 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 @@ | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | 12 | package cuchaz.enigma.translation.representation.entry; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.strobel.assembler.metadata.MethodDefinition; | ||
| 16 | import cuchaz.enigma.translation.Translator; | 15 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | 16 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 18 | import cuchaz.enigma.translation.representation.AccessFlags; | 17 | import 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 @@ | |||
| 1 | package cuchaz.enigma.utils; | ||
| 2 | |||
| 3 | import java.util.Objects; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package oml; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.Buffer; | ||
| 4 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 5 | |||
| 6 | import java.io.File; | ||
| 7 | import java.io.IOException; | ||
| 8 | import java.io.InputStream; | ||
| 9 | import java.net.MalformedURLException; | ||
| 10 | import java.net.URL; | ||
| 11 | import java.net.URLClassLoader; | ||
| 12 | import java.util.Arrays; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Copy of ClasspathTypeLoader supporting a classpath constructor. | ||
| 16 | */ | ||
| 17 | public 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", |
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java index 3ee86cb7..c88b0eb6 100644 --- a/src/test/java/cuchaz/enigma/TestDeobfed.java +++ b/src/test/java/cuchaz/enigma/TestDeobfed.java | |||
| @@ -13,6 +13,9 @@ package cuchaz.enigma; | |||
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.ClassCache; | 14 | import cuchaz.enigma.analysis.ClassCache; |
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.source.Decompiler; | ||
| 17 | import cuchaz.enigma.source.Decompilers; | ||
| 18 | import cuchaz.enigma.source.SourceSettings; | ||
| 16 | import org.junit.BeforeClass; | 19 | import org.junit.BeforeClass; |
| 17 | import org.junit.Test; | 20 | import org.junit.Test; |
| 18 | 21 | ||
| @@ -68,31 +71,29 @@ public class TestDeobfed { | |||
| 68 | @Test | 71 | @Test |
| 69 | public void decompile() { | 72 | public void decompile() { |
| 70 | EnigmaProject project = new EnigmaProject(enigma, classCache, index); | 73 | EnigmaProject project = new EnigmaProject(enigma, classCache, index); |
| 74 | Decompiler decompiler = Decompilers.PROCYON.create(project.getClassCache(), new SourceSettings(false, false)); | ||
| 71 | 75 | ||
| 72 | CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(project.getClassCache()); | 76 | decompiler.getSource("a"); |
| 73 | SourceProvider sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader); | 77 | decompiler.getSource("b"); |
| 74 | 78 | decompiler.getSource("c"); | |
| 75 | sourceProvider.getSources("a"); | 79 | decompiler.getSource("d"); |
| 76 | sourceProvider.getSources("b"); | 80 | decompiler.getSource("d$1"); |
| 77 | sourceProvider.getSources("c"); | 81 | decompiler.getSource("e"); |
| 78 | sourceProvider.getSources("d"); | 82 | decompiler.getSource("f"); |
| 79 | sourceProvider.getSources("d$1"); | 83 | decompiler.getSource("g"); |
| 80 | sourceProvider.getSources("e"); | 84 | decompiler.getSource("g$a"); |
| 81 | sourceProvider.getSources("f"); | 85 | decompiler.getSource("g$a$a"); |
| 82 | sourceProvider.getSources("g"); | 86 | decompiler.getSource("g$b"); |
| 83 | sourceProvider.getSources("g$a"); | 87 | decompiler.getSource("g$b$a"); |
| 84 | sourceProvider.getSources("g$a$a"); | 88 | decompiler.getSource("h"); |
| 85 | sourceProvider.getSources("g$b"); | 89 | decompiler.getSource("h$a"); |
| 86 | sourceProvider.getSources("g$b$a"); | 90 | decompiler.getSource("h$a$a"); |
| 87 | sourceProvider.getSources("h"); | 91 | decompiler.getSource("h$b"); |
| 88 | sourceProvider.getSources("h$a"); | 92 | decompiler.getSource("h$b$a"); |
| 89 | sourceProvider.getSources("h$a$a"); | 93 | decompiler.getSource("h$b$a$a"); |
| 90 | sourceProvider.getSources("h$b"); | 94 | decompiler.getSource("h$b$a$b"); |
| 91 | sourceProvider.getSources("h$b$a"); | 95 | decompiler.getSource("i"); |
| 92 | sourceProvider.getSources("h$b$a$a"); | 96 | decompiler.getSource("i$a"); |
| 93 | sourceProvider.getSources("h$b$a$b"); | 97 | decompiler.getSource("i$b"); |
| 94 | sourceProvider.getSources("i"); | ||
| 95 | sourceProvider.getSources("i$a"); | ||
| 96 | sourceProvider.getSources("i$b"); | ||
| 97 | } | 98 | } |
| 98 | } | 99 | } |
diff --git a/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/src/test/java/cuchaz/enigma/TestDeobfuscator.java index 2a6fec49..6619d26e 100644 --- a/src/test/java/cuchaz/enigma/TestDeobfuscator.java +++ b/src/test/java/cuchaz/enigma/TestDeobfuscator.java | |||
| @@ -11,13 +11,15 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.source.Decompiler; | ||
| 15 | import cuchaz.enigma.source.Decompilers; | ||
| 16 | import cuchaz.enigma.source.SourceSettings; | ||
| 14 | import org.junit.Test; | 17 | import org.junit.Test; |
| 15 | 18 | ||
| 16 | import java.io.IOException; | 19 | import java.io.IOException; |
| 17 | import java.nio.file.Paths; | 20 | import java.nio.file.Paths; |
| 18 | 21 | ||
| 19 | public class TestDeobfuscator { | 22 | public class TestDeobfuscator { |
| 20 | |||
| 21 | private EnigmaProject openProject() throws IOException { | 23 | private EnigmaProject openProject() throws IOException { |
| 22 | Enigma enigma = Enigma.create(); | 24 | Enigma enigma = Enigma.create(); |
| 23 | return enigma.openJar(Paths.get("build/test-obf/loneClass.jar"), ProgressListener.none()); | 25 | return enigma.openJar(Paths.get("build/test-obf/loneClass.jar"), ProgressListener.none()); |
| @@ -32,10 +34,8 @@ public class TestDeobfuscator { | |||
| 32 | @Test | 34 | @Test |
| 33 | public void decompileClass() throws Exception { | 35 | public void decompileClass() throws Exception { |
| 34 | EnigmaProject project = openProject(); | 36 | EnigmaProject project = openProject(); |
| 37 | Decompiler decompiler = Decompilers.PROCYON.create(project.getClassCache(), new SourceSettings(false, false)); | ||
| 35 | 38 | ||
| 36 | CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(project.getClassCache()); | 39 | decompiler.getSource("a").asString(); |
| 37 | SourceProvider sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader); | ||
| 38 | |||
| 39 | sourceProvider.writeSourceToString(sourceProvider.getSources("a")); | ||
| 40 | } | 40 | } |
| 41 | } | 41 | } |
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java index 18e49369..85c72f81 100644 --- a/src/test/java/cuchaz/enigma/TestInnerClasses.java +++ b/src/test/java/cuchaz/enigma/TestInnerClasses.java | |||
| @@ -13,6 +13,9 @@ package cuchaz.enigma; | |||
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.ClassCache; | 14 | import cuchaz.enigma.analysis.ClassCache; |
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.source.Decompiler; | ||
| 17 | import cuchaz.enigma.source.Decompilers; | ||
| 18 | import cuchaz.enigma.source.SourceSettings; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 17 | import org.junit.Test; | 20 | import org.junit.Test; |
| 18 | 21 | ||
| @@ -32,15 +35,13 @@ public class TestInnerClasses { | |||
| 32 | private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); | 35 | private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); |
| 33 | private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); | 36 | private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); |
| 34 | private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a"); | 37 | private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a"); |
| 35 | private JarIndex index; | 38 | private final JarIndex index; |
| 36 | private SourceProvider sourceProvider; | 39 | private final Decompiler decompiler; |
| 37 | 40 | ||
| 38 | public TestInnerClasses() throws Exception { | 41 | public TestInnerClasses() throws Exception { |
| 39 | ClassCache classCache = ClassCache.of(Paths.get("build/test-obf/innerClasses.jar")); | 42 | ClassCache classCache = ClassCache.of(Paths.get("build/test-obf/innerClasses.jar")); |
| 40 | index = classCache.index(ProgressListener.none()); | 43 | index = classCache.index(ProgressListener.none()); |
| 41 | 44 | decompiler = Decompilers.PROCYON.create(classCache, new SourceSettings(false, false)); | |
| 42 | CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(classCache); | ||
| 43 | sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader); | ||
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | @Test | 47 | @Test |
| @@ -79,6 +80,6 @@ public class TestInnerClasses { | |||
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | private void decompile(ClassEntry classEntry) { | 82 | private void decompile(ClassEntry classEntry) { |
| 82 | sourceProvider.getSources(classEntry.getName()); | 83 | decompiler.getSource(classEntry.getName()); |
| 83 | } | 84 | } |
| 84 | } | 85 | } |
diff --git a/src/test/java/cuchaz/enigma/TestSourceIndex.java b/src/test/java/cuchaz/enigma/TestSourceIndex.java index b1d11822..b2016089 100644 --- a/src/test/java/cuchaz/enigma/TestSourceIndex.java +++ b/src/test/java/cuchaz/enigma/TestSourceIndex.java | |||
| @@ -12,9 +12,8 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 16 | import cuchaz.enigma.analysis.ClassCache; | 15 | import cuchaz.enigma.analysis.ClassCache; |
| 17 | import cuchaz.enigma.analysis.SourceIndex; | 16 | import cuchaz.enigma.source.*; |
| 18 | import cuchaz.enigma.analysis.index.JarIndex; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 20 | import org.junit.Test; | 19 | import org.junit.Test; |
| @@ -53,8 +52,7 @@ public class TestSourceIndex { | |||
| 53 | ClassCache classCache = project.getClassCache(); | 52 | ClassCache classCache = project.getClassCache(); |
| 54 | JarIndex index = project.getJarIndex(); | 53 | JarIndex index = project.getJarIndex(); |
| 55 | 54 | ||
| 56 | CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(classCache); | 55 | Decompiler decompiler = Decompilers.PROCYON.create(classCache, new SourceSettings(false, false)); |
| 57 | SourceProvider sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader); | ||
| 58 | 56 | ||
| 59 | // get all classes that aren't inner classes | 57 | // get all classes that aren't inner classes |
| 60 | Set<ClassEntry> classEntries = Sets.newHashSet(); | 58 | Set<ClassEntry> classEntries = Sets.newHashSet(); |
| @@ -66,10 +64,8 @@ public class TestSourceIndex { | |||
| 66 | 64 | ||
| 67 | for (ClassEntry obfClassEntry : classEntries) { | 65 | for (ClassEntry obfClassEntry : classEntries) { |
| 68 | try { | 66 | try { |
| 69 | CompilationUnit tree = sourceProvider.getSources(obfClassEntry.getName()); | 67 | Source source = decompiler.getSource(obfClassEntry.getName()); |
| 70 | String source = sourceProvider.writeSourceToString(tree); | 68 | source.index(); |
| 71 | |||
| 72 | SourceIndex.buildIndex(source, tree, true); | ||
| 73 | } catch (Throwable t) { | 69 | } catch (Throwable t) { |
| 74 | throw new Error("Unable to index " + obfClassEntry, t); | 70 | throw new Error("Unable to index " + obfClassEntry, t); |
| 75 | } | 71 | } |
diff --git a/src/test/java/cuchaz/enigma/TokenChecker.java b/src/test/java/cuchaz/enigma/TokenChecker.java index 1dde0349..48d0c830 100644 --- a/src/test/java/cuchaz/enigma/TokenChecker.java +++ b/src/test/java/cuchaz/enigma/TokenChecker.java | |||
| @@ -12,10 +12,10 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 16 | import cuchaz.enigma.analysis.ClassCache; | 15 | import cuchaz.enigma.analysis.ClassCache; |
| 17 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 18 | import cuchaz.enigma.analysis.SourceIndex; | 17 | import cuchaz.enigma.source.SourceIndex; |
| 18 | import cuchaz.enigma.source.*; | ||
| 19 | import cuchaz.enigma.analysis.Token; | 19 | import cuchaz.enigma.analysis.Token; |
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | 20 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 21 | 21 | ||
| @@ -25,43 +25,40 @@ import java.util.Collection; | |||
| 25 | import java.util.List; | 25 | import java.util.List; |
| 26 | 26 | ||
| 27 | public class TokenChecker { | 27 | public class TokenChecker { |
| 28 | 28 | private final Decompiler decompiler; | |
| 29 | private SourceProvider sourceProvider; | ||
| 30 | 29 | ||
| 31 | protected TokenChecker(Path path) throws IOException { | 30 | protected TokenChecker(Path path) throws IOException { |
| 32 | ClassCache classCache = ClassCache.of(path); | 31 | ClassCache classCache = ClassCache.of(path); |
| 33 | 32 | decompiler = Decompilers.PROCYON.create(classCache, new SourceSettings(false, false)); | |
| 34 | CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(classCache); | ||
| 35 | sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader); | ||
| 36 | } | 33 | } |
| 37 | 34 | ||
| 38 | protected String getDeclarationToken(Entry<?> entry) { | 35 | protected String getDeclarationToken(Entry<?> entry) { |
| 39 | // decompile the class | 36 | // decompile the class |
| 40 | CompilationUnit tree = sourceProvider.getSources(entry.getContainingClass().getFullName()); | 37 | Source source = decompiler.getSource(entry.getContainingClass().getFullName()); |
| 41 | // DEBUG | 38 | // DEBUG |
| 42 | // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); | 39 | // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); |
| 43 | String source = sourceProvider.writeSourceToString(tree); | 40 | String string = source.asString(); |
| 44 | SourceIndex index = SourceIndex.buildIndex(source, tree, true); | 41 | SourceIndex index = source.index(); |
| 45 | 42 | ||
| 46 | // get the token value | 43 | // get the token value |
| 47 | Token token = index.getDeclarationToken(entry); | 44 | Token token = index.getDeclarationToken(entry); |
| 48 | if (token == null) { | 45 | if (token == null) { |
| 49 | return null; | 46 | return null; |
| 50 | } | 47 | } |
| 51 | return source.substring(token.start, token.end); | 48 | return string.substring(token.start, token.end); |
| 52 | } | 49 | } |
| 53 | 50 | ||
| 54 | @SuppressWarnings("unchecked") | 51 | @SuppressWarnings("unchecked") |
| 55 | protected Collection<String> getReferenceTokens(EntryReference<? extends Entry<?>, ? extends Entry<?>> reference) { | 52 | protected Collection<String> getReferenceTokens(EntryReference<? extends Entry<?>, ? extends Entry<?>> reference) { |
| 56 | // decompile the class | 53 | // decompile the class |
| 57 | CompilationUnit tree = sourceProvider.getSources(reference.context.getContainingClass().getFullName()); | 54 | Source source = decompiler.getSource(reference.context.getContainingClass().getFullName()); |
| 58 | String source = sourceProvider.writeSourceToString(tree); | 55 | String string = source.asString(); |
| 59 | SourceIndex index = SourceIndex.buildIndex(source, tree, true); | 56 | SourceIndex index = source.index(); |
| 60 | 57 | ||
| 61 | // get the token values | 58 | // get the token values |
| 62 | List<String> values = Lists.newArrayList(); | 59 | List<String> values = Lists.newArrayList(); |
| 63 | for (Token token : index.getReferenceTokens((EntryReference<Entry<?>, Entry<?>>) reference)) { | 60 | for (Token token : index.getReferenceTokens((EntryReference<Entry<?>, Entry<?>>) reference)) { |
| 64 | values.add(source.substring(token.start, token.end)); | 61 | values.add(string.substring(token.start, token.end)); |
| 65 | } | 62 | } |
| 66 | return values; | 63 | return values; |
| 67 | } | 64 | } |