diff options
| author | 2020-07-06 06:34:10 -0400 | |
|---|---|---|
| committer | 2020-07-06 12:34:10 +0200 | |
| commit | f0885819aeeb2edbfcfc0b23566cccb571166a02 (patch) | |
| tree | d650aa68641fdfd9cc5f5ed0093229d1840506d8 | |
| parent | Fix "Mark as Deobfuscated" menu entry not working... this time without breaki... (diff) | |
| download | enigma-f0885819aeeb2edbfcfc0b23566cccb571166a02.tar.gz enigma-f0885819aeeb2edbfcfc0b23566cccb571166a02.tar.xz enigma-f0885819aeeb2edbfcfc0b23566cccb571166a02.zip | |
Make class loading more flexible (#277)
37 files changed, 440 insertions, 535 deletions
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java index e4deef8b..75ef225a 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java | |||
| @@ -4,6 +4,7 @@ import cuchaz.enigma.Enigma; | |||
| 4 | import cuchaz.enigma.EnigmaProject; | 4 | import cuchaz.enigma.EnigmaProject; |
| 5 | import cuchaz.enigma.ProgressListener; | 5 | import cuchaz.enigma.ProgressListener; |
| 6 | import cuchaz.enigma.analysis.index.JarIndex; | 6 | import cuchaz.enigma.analysis.index.JarIndex; |
| 7 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryMapping; | 8 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 8 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; | 9 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; |
| 9 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | 10 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; |
| @@ -39,7 +40,7 @@ public class CheckMappingsCommand extends Command { | |||
| 39 | 40 | ||
| 40 | System.out.println("Reading JAR..."); | 41 | System.out.println("Reading JAR..."); |
| 41 | 42 | ||
| 42 | EnigmaProject project = enigma.openJar(fileJarIn, ProgressListener.none()); | 43 | EnigmaProject project = enigma.openJar(fileJarIn, new ClasspathClassProvider(), ProgressListener.none()); |
| 43 | 44 | ||
| 44 | System.out.println("Reading mappings..."); | 45 | System.out.println("Reading mappings..."); |
| 45 | 46 | ||
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java index 0640e3e7..0d71f028 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java | |||
| @@ -3,6 +3,7 @@ package cuchaz.enigma.command; | |||
| 3 | import cuchaz.enigma.Enigma; | 3 | import cuchaz.enigma.Enigma; |
| 4 | import cuchaz.enigma.EnigmaProject; | 4 | import cuchaz.enigma.EnigmaProject; |
| 5 | import cuchaz.enigma.ProgressListener; | 5 | import cuchaz.enigma.ProgressListener; |
| 6 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | 7 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 7 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; | 8 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; |
| 8 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | 9 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; |
| @@ -34,7 +35,7 @@ public abstract class Command { | |||
| 34 | Enigma enigma = Enigma.create(); | 35 | Enigma enigma = Enigma.create(); |
| 35 | 36 | ||
| 36 | System.out.println("Reading jar..."); | 37 | System.out.println("Reading jar..."); |
| 37 | EnigmaProject project = enigma.openJar(fileJarIn, progress); | 38 | EnigmaProject project = enigma.openJar(fileJarIn, new ClasspathClassProvider(), progress); |
| 38 | 39 | ||
| 39 | if (fileMappings != null) { | 40 | if (fileMappings != null) { |
| 40 | System.out.println("Reading mappings..."); | 41 | System.out.println("Reading mappings..."); |
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java index 292de192..94f70100 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java | |||
| @@ -1,14 +1,15 @@ | |||
| 1 | package cuchaz.enigma.command; | 1 | package cuchaz.enigma.command; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.ProgressListener; | 3 | import cuchaz.enigma.ProgressListener; |
| 4 | import cuchaz.enigma.analysis.ClassCache; | ||
| 5 | import cuchaz.enigma.analysis.index.BridgeMethodIndex; | 4 | import cuchaz.enigma.analysis.index.BridgeMethodIndex; |
| 6 | import cuchaz.enigma.analysis.index.JarIndex; | 5 | import cuchaz.enigma.analysis.index.JarIndex; |
| 7 | import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; | 6 | import cuchaz.enigma.classprovider.CachingClassProvider; |
| 8 | import cuchaz.enigma.translation.mapping.serde.MappingParseException; | 7 | import cuchaz.enigma.classprovider.JarClassProvider; |
| 9 | import cuchaz.enigma.translation.MappingTranslator; | 8 | import cuchaz.enigma.translation.MappingTranslator; |
| 10 | import cuchaz.enigma.translation.Translator; | 9 | import cuchaz.enigma.translation.Translator; |
| 11 | import cuchaz.enigma.translation.mapping.*; | 10 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 11 | import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; | ||
| 12 | import cuchaz.enigma.translation.mapping.serde.MappingParseException; | ||
| 12 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; | 13 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; |
| 13 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 14 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 14 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | 15 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; |
| @@ -45,8 +46,11 @@ public class MapSpecializedMethodsCommand extends Command { | |||
| 45 | MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); | 46 | MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); |
| 46 | EntryTree<EntryMapping> source = MappingCommandsUtil.read(sourceFormat, sourcePath, saveParameters); | 47 | EntryTree<EntryMapping> source = MappingCommandsUtil.read(sourceFormat, sourcePath, saveParameters); |
| 47 | EntryTree<EntryMapping> result = new HashEntryTree<>(); | 48 | EntryTree<EntryMapping> result = new HashEntryTree<>(); |
| 48 | ClassCache classCache = ClassCache.of(jar); | 49 | |
| 49 | JarIndex jarIndex = classCache.index(ProgressListener.none()); | 50 | JarClassProvider jcp = new JarClassProvider(jar); |
| 51 | JarIndex jarIndex = JarIndex.empty(); | ||
| 52 | jarIndex.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none()); | ||
| 53 | |||
| 50 | BridgeMethodIndex bridgeMethodIndex = jarIndex.getBridgeMethodIndex(); | 54 | BridgeMethodIndex bridgeMethodIndex = jarIndex.getBridgeMethodIndex(); |
| 51 | Translator translator = new MappingTranslator(source, jarIndex.getEntryResolver()); | 55 | Translator translator = new MappingTranslator(source, jarIndex.getEntryResolver()); |
| 52 | 56 | ||
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java index 924302f3..41f08342 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java | |||
| @@ -2,6 +2,7 @@ package cuchaz.enigma.network; | |||
| 2 | 2 | ||
| 3 | import com.google.common.io.MoreFiles; | 3 | import com.google.common.io.MoreFiles; |
| 4 | import cuchaz.enigma.*; | 4 | import cuchaz.enigma.*; |
| 5 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
| 5 | import cuchaz.enigma.translation.mapping.serde.MappingParseException; | 6 | import cuchaz.enigma.translation.mapping.serde.MappingParseException; |
| 6 | import cuchaz.enigma.translation.mapping.EntryRemapper; | 7 | import cuchaz.enigma.translation.mapping.EntryRemapper; |
| 7 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | 8 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; |
| @@ -108,7 +109,7 @@ public class DedicatedEnigmaServer extends EnigmaServer { | |||
| 108 | EnigmaProfile profile = EnigmaProfile.read(profileFile); | 109 | EnigmaProfile profile = EnigmaProfile.read(profileFile); |
| 109 | Enigma enigma = Enigma.builder().setProfile(profile).build(); | 110 | Enigma enigma = Enigma.builder().setProfile(profile).build(); |
| 110 | System.out.println("Indexing Jar..."); | 111 | System.out.println("Indexing Jar..."); |
| 111 | EnigmaProject project = enigma.openJar(jar, ProgressListener.none()); | 112 | EnigmaProject project = enigma.openJar(jar, new ClasspathClassProvider(), ProgressListener.none()); |
| 112 | 113 | ||
| 113 | MappingFormat mappingFormat = MappingFormat.ENIGMA_DIRECTORY; | 114 | MappingFormat mappingFormat = MappingFormat.ENIGMA_DIRECTORY; |
| 114 | EntryRemapper mappings; | 115 | EntryRemapper mappings; |
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index 66a21b00..2dc1d77a 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -34,6 +34,7 @@ import cuchaz.enigma.EnigmaProfile; | |||
| 34 | import cuchaz.enigma.EnigmaProject; | 34 | import cuchaz.enigma.EnigmaProject; |
| 35 | import cuchaz.enigma.analysis.*; | 35 | import cuchaz.enigma.analysis.*; |
| 36 | import cuchaz.enigma.api.service.ObfuscationTestService; | 36 | import cuchaz.enigma.api.service.ObfuscationTestService; |
| 37 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
| 37 | import cuchaz.enigma.classhandle.ClassHandle; | 38 | import cuchaz.enigma.classhandle.ClassHandle; |
| 38 | import cuchaz.enigma.classhandle.ClassHandleProvider; | 39 | import cuchaz.enigma.classhandle.ClassHandleProvider; |
| 39 | import cuchaz.enigma.gui.config.Config; | 40 | import cuchaz.enigma.gui.config.Config; |
| @@ -95,7 +96,7 @@ public class GuiController implements ClientPacketHandler { | |||
| 95 | this.gui.onStartOpenJar(); | 96 | this.gui.onStartOpenJar(); |
| 96 | 97 | ||
| 97 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { | 98 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { |
| 98 | project = enigma.openJar(jarPath, progress); | 99 | project = enigma.openJar(jarPath, new ClasspathClassProvider(), progress); |
| 99 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); | 100 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); |
| 100 | chp = new ClassHandleProvider(project, Config.getInstance().decompiler.service); | 101 | chp = new ClassHandleProvider(project, Config.getInstance().decompiler.service); |
| 101 | gui.onFinishOpenJar(jarPath.getFileName().toString()); | 102 | gui.onFinishOpenJar(jarPath.getFileName().toString()); |
diff --git a/enigma/src/main/java/cuchaz/enigma/ClassProvider.java b/enigma/src/main/java/cuchaz/enigma/ClassProvider.java deleted file mode 100644 index 2b913792..00000000 --- a/enigma/src/main/java/cuchaz/enigma/ClassProvider.java +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import org.objectweb.asm.tree.ClassNode; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | |||
| 7 | public interface ClassProvider { | ||
| 8 | @Nullable | ||
| 9 | ClassNode getClassNode(String name); | ||
| 10 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/Enigma.java b/enigma/src/main/java/cuchaz/enigma/Enigma.java index 73c9a090..2e9be34f 100644 --- a/enigma/src/main/java/cuchaz/enigma/Enigma.java +++ b/enigma/src/main/java/cuchaz/enigma/Enigma.java | |||
| @@ -13,7 +13,6 @@ package cuchaz.enigma; | |||
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.google.common.collect.ImmutableListMultimap; | 15 | import com.google.common.collect.ImmutableListMultimap; |
| 16 | import cuchaz.enigma.analysis.ClassCache; | ||
| 17 | import cuchaz.enigma.analysis.index.JarIndex; | 16 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.api.EnigmaPlugin; | 17 | import cuchaz.enigma.api.EnigmaPlugin; |
| 19 | import cuchaz.enigma.api.EnigmaPluginContext; | 18 | import cuchaz.enigma.api.EnigmaPluginContext; |
| @@ -21,6 +20,10 @@ import cuchaz.enigma.api.service.EnigmaService; | |||
| 21 | import cuchaz.enigma.api.service.EnigmaServiceFactory; | 20 | import cuchaz.enigma.api.service.EnigmaServiceFactory; |
| 22 | import cuchaz.enigma.api.service.EnigmaServiceType; | 21 | import cuchaz.enigma.api.service.EnigmaServiceType; |
| 23 | import cuchaz.enigma.api.service.JarIndexerService; | 22 | import cuchaz.enigma.api.service.JarIndexerService; |
| 23 | import cuchaz.enigma.classprovider.CachingClassProvider; | ||
| 24 | import cuchaz.enigma.classprovider.ClassProvider; | ||
| 25 | import cuchaz.enigma.classprovider.CombiningClassProvider; | ||
| 26 | import cuchaz.enigma.classprovider.JarClassProvider; | ||
| 24 | import cuchaz.enigma.utils.Utils; | 27 | import cuchaz.enigma.utils.Utils; |
| 25 | import org.objectweb.asm.Opcodes; | 28 | import org.objectweb.asm.Opcodes; |
| 26 | 29 | ||
| @@ -28,6 +31,7 @@ import java.io.IOException; | |||
| 28 | import java.nio.file.Path; | 31 | import java.nio.file.Path; |
| 29 | import java.util.List; | 32 | import java.util.List; |
| 30 | import java.util.ServiceLoader; | 33 | import java.util.ServiceLoader; |
| 34 | import java.util.Set; | ||
| 31 | 35 | ||
| 32 | public class Enigma { | 36 | public class Enigma { |
| 33 | public static final String NAME = "Enigma"; | 37 | public static final String NAME = "Enigma"; |
| @@ -51,13 +55,16 @@ public class Enigma { | |||
| 51 | return new Builder(); | 55 | return new Builder(); |
| 52 | } | 56 | } |
| 53 | 57 | ||
| 54 | public EnigmaProject openJar(Path path, ProgressListener progress) throws IOException { | 58 | public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, ProgressListener progress) throws IOException { |
| 55 | ClassCache classCache = ClassCache.of(path); | 59 | JarClassProvider jarClassProvider = new JarClassProvider(path); |
| 56 | JarIndex jarIndex = classCache.index(progress); | 60 | ClassProvider classProvider = new CachingClassProvider(new CombiningClassProvider(jarClassProvider, libraryClassProvider)); |
| 61 | Set<String> scope = jarClassProvider.getClassNames(); | ||
| 57 | 62 | ||
| 58 | services.get(JarIndexerService.TYPE).forEach(indexer -> indexer.acceptJar(classCache, jarIndex)); | 63 | JarIndex index = JarIndex.empty(); |
| 64 | index.indexJar(scope, classProvider, progress); | ||
| 65 | services.get(JarIndexerService.TYPE).forEach(indexer -> indexer.acceptJar(scope, classProvider, index)); | ||
| 59 | 66 | ||
| 60 | return new EnigmaProject(this, classCache, jarIndex, Utils.zipSha1(path)); | 67 | return new EnigmaProject(this, classProvider, index, Utils.zipSha1(path)); |
| 61 | } | 68 | } |
| 62 | 69 | ||
| 63 | public EnigmaProfile getProfile() { | 70 | public EnigmaProfile getProfile() { |
diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 43ea14ed..3999572f 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java | |||
| @@ -2,16 +2,20 @@ package cuchaz.enigma; | |||
| 2 | 2 | ||
| 3 | import com.google.common.base.Functions; | 3 | import com.google.common.base.Functions; |
| 4 | import com.google.common.base.Preconditions; | 4 | import com.google.common.base.Preconditions; |
| 5 | import cuchaz.enigma.analysis.ClassCache; | ||
| 6 | import cuchaz.enigma.analysis.EntryReference; | 5 | import cuchaz.enigma.analysis.EntryReference; |
| 7 | import cuchaz.enigma.analysis.index.JarIndex; | 6 | import cuchaz.enigma.analysis.index.JarIndex; |
| 8 | import cuchaz.enigma.api.service.NameProposalService; | 7 | import cuchaz.enigma.api.service.NameProposalService; |
| 9 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; | 8 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; |
| 10 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; | 9 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; |
| 11 | import cuchaz.enigma.source.*; | 10 | import cuchaz.enigma.classprovider.ClassProvider; |
| 11 | import cuchaz.enigma.source.Decompiler; | ||
| 12 | import cuchaz.enigma.source.DecompilerService; | ||
| 13 | import cuchaz.enigma.source.SourceSettings; | ||
| 12 | import cuchaz.enigma.translation.ProposingTranslator; | 14 | import cuchaz.enigma.translation.ProposingTranslator; |
| 13 | import cuchaz.enigma.translation.Translator; | 15 | import cuchaz.enigma.translation.Translator; |
| 14 | import cuchaz.enigma.translation.mapping.*; | 16 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 17 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 18 | import cuchaz.enigma.translation.mapping.MappingsChecker; | ||
| 15 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | 19 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; |
| 16 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 20 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 21 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| @@ -19,11 +23,11 @@ import cuchaz.enigma.translation.representation.entry.Entry; | |||
| 19 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | 23 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; |
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 24 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 21 | import cuchaz.enigma.utils.I18n; | 25 | import cuchaz.enigma.utils.I18n; |
| 22 | |||
| 23 | import org.objectweb.asm.ClassWriter; | 26 | import org.objectweb.asm.ClassWriter; |
| 24 | import org.objectweb.asm.tree.ClassNode; | 27 | import org.objectweb.asm.tree.ClassNode; |
| 25 | 28 | ||
| 26 | import java.io.*; | 29 | import java.io.BufferedWriter; |
| 30 | import java.io.IOException; | ||
| 27 | import java.nio.file.Files; | 31 | import java.nio.file.Files; |
| 28 | import java.nio.file.Path; | 32 | import java.nio.file.Path; |
| 29 | import java.util.Collection; | 33 | import java.util.Collection; |
| @@ -37,16 +41,16 @@ import java.util.stream.Collectors; | |||
| 37 | public class EnigmaProject { | 41 | public class EnigmaProject { |
| 38 | private final Enigma enigma; | 42 | private final Enigma enigma; |
| 39 | 43 | ||
| 40 | private final ClassCache classCache; | 44 | private final ClassProvider classProvider; |
| 41 | private final JarIndex jarIndex; | 45 | private final JarIndex jarIndex; |
| 42 | private final byte[] jarChecksum; | 46 | private final byte[] jarChecksum; |
| 43 | 47 | ||
| 44 | private EntryRemapper mapper; | 48 | private EntryRemapper mapper; |
| 45 | 49 | ||
| 46 | public EnigmaProject(Enigma enigma, ClassCache classCache, JarIndex jarIndex, byte[] jarChecksum) { | 50 | public EnigmaProject(Enigma enigma, ClassProvider classProvider, JarIndex jarIndex, byte[] jarChecksum) { |
| 47 | Preconditions.checkArgument(jarChecksum.length == 20); | 51 | Preconditions.checkArgument(jarChecksum.length == 20); |
| 48 | this.enigma = enigma; | 52 | this.enigma = enigma; |
| 49 | this.classCache = classCache; | 53 | this.classProvider = classProvider; |
| 50 | this.jarIndex = jarIndex; | 54 | this.jarIndex = jarIndex; |
| 51 | this.jarChecksum = jarChecksum; | 55 | this.jarChecksum = jarChecksum; |
| 52 | 56 | ||
| @@ -65,8 +69,8 @@ public class EnigmaProject { | |||
| 65 | return enigma; | 69 | return enigma; |
| 66 | } | 70 | } |
| 67 | 71 | ||
| 68 | public ClassCache getClassCache() { | 72 | public ClassProvider getClassProvider() { |
| 69 | return classCache; | 73 | return classProvider; |
| 70 | } | 74 | } |
| 71 | 75 | ||
| 72 | public JarIndex getJarIndex() { | 76 | public JarIndex getJarIndex() { |
| @@ -103,20 +107,6 @@ public class EnigmaProject { | |||
| 103 | return droppedMappings.keySet(); | 107 | return droppedMappings.keySet(); |
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | public Decompiler createDecompiler(DecompilerService decompilerService) { | ||
| 107 | return decompilerService.create(name -> { | ||
| 108 | ClassNode node = this.getClassCache().getClassNode(name); | ||
| 109 | |||
| 110 | if (node == null) { | ||
| 111 | return null; | ||
| 112 | } | ||
| 113 | |||
| 114 | ClassNode fixedNode = new ClassNode(); | ||
| 115 | node.accept(new SourceFixVisitor(Enigma.ASM_VERSION, fixedNode, getJarIndex())); | ||
| 116 | return fixedNode; | ||
| 117 | }, new SourceSettings(true, true)); | ||
| 118 | } | ||
| 119 | |||
| 120 | public boolean isRenamable(Entry<?> obfEntry) { | 110 | public boolean isRenamable(Entry<?> obfEntry) { |
| 121 | if (obfEntry instanceof MethodEntry) { | 111 | if (obfEntry instanceof MethodEntry) { |
| 122 | // HACKHACK: Object methods are not obfuscated identifiers | 112 | // HACKHACK: Object methods are not obfuscated identifiers |
| @@ -171,7 +161,7 @@ public class EnigmaProject { | |||
| 171 | ClassEntry translatedEntry = deobfuscator.translate(entry); | 161 | ClassEntry translatedEntry = deobfuscator.translate(entry); |
| 172 | progress.step(count.getAndIncrement(), translatedEntry.toString()); | 162 | progress.step(count.getAndIncrement(), translatedEntry.toString()); |
| 173 | 163 | ||
| 174 | ClassNode node = classCache.getClassNode(entry.getFullName()); | 164 | ClassNode node = classProvider.get(entry.getFullName()); |
| 175 | if (node != null) { | 165 | if (node != null) { |
| 176 | ClassNode translatedNode = new ClassNode(); | 166 | ClassNode translatedNode = new ClassNode(); |
| 177 | node.accept(new TranslationClassVisitor(deobfuscator, Enigma.ASM_VERSION, new SourceFixVisitor(Enigma.ASM_VERSION, translatedNode, jarIndex))); | 167 | node.accept(new TranslationClassVisitor(deobfuscator, Enigma.ASM_VERSION, new SourceFixVisitor(Enigma.ASM_VERSION, translatedNode, jarIndex))); |
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java index 4685b390..989464de 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java | |||
| @@ -12,7 +12,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; | |||
| 12 | import cuchaz.enigma.translation.representation.entry.Entry; | 12 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 13 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | 13 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 14 | import cuchaz.enigma.utils.Pair; | 14 | import cuchaz.enigma.utils.Pair; |
| 15 | import org.objectweb.asm.ClassReader; | ||
| 16 | import org.objectweb.asm.ClassVisitor; | 15 | import org.objectweb.asm.ClassVisitor; |
| 17 | import org.objectweb.asm.FieldVisitor; | 16 | import org.objectweb.asm.FieldVisitor; |
| 18 | import org.objectweb.asm.MethodVisitor; | 17 | import org.objectweb.asm.MethodVisitor; |
| @@ -51,7 +50,7 @@ public final class BuiltinPlugin implements EnigmaPlugin { | |||
| 51 | final Map<Entry<?>, String> names = new HashMap<>(); | 50 | final Map<Entry<?>, String> names = new HashMap<>(); |
| 52 | final EnumFieldNameFindingVisitor visitor = new EnumFieldNameFindingVisitor(names); | 51 | final EnumFieldNameFindingVisitor visitor = new EnumFieldNameFindingVisitor(names); |
| 53 | 52 | ||
| 54 | ctx.registerService("enigma:enum_initializer_indexer", JarIndexerService.TYPE, ctx1 -> (classCache, jarIndex) -> classCache.visit(() -> visitor, ClassReader.SKIP_FRAMES)); | 53 | ctx.registerService("enigma:enum_initializer_indexer", JarIndexerService.TYPE, ctx1 -> JarIndexerService.fromVisitor(visitor)); |
| 55 | ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry))); | 54 | ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry))); |
| 56 | } | 55 | } |
| 57 | 56 | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ClassCache.java b/enigma/src/main/java/cuchaz/enigma/analysis/ClassCache.java deleted file mode 100644 index a3d24e32..00000000 --- a/enigma/src/main/java/cuchaz/enigma/analysis/ClassCache.java +++ /dev/null | |||
| @@ -1,126 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.google.common.cache.Cache; | ||
| 4 | import com.google.common.cache.CacheBuilder; | ||
| 5 | import com.google.common.collect.ImmutableSet; | ||
| 6 | import cuchaz.enigma.ClassProvider; | ||
| 7 | import cuchaz.enigma.Enigma; | ||
| 8 | import cuchaz.enigma.ProgressListener; | ||
| 9 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 10 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; | ||
| 11 | import org.objectweb.asm.ClassReader; | ||
| 12 | import org.objectweb.asm.ClassVisitor; | ||
| 13 | import org.objectweb.asm.tree.ClassNode; | ||
| 14 | |||
| 15 | import javax.annotation.Nullable; | ||
| 16 | import java.io.IOException; | ||
| 17 | import java.nio.file.FileSystem; | ||
| 18 | import java.nio.file.FileSystems; | ||
| 19 | import java.nio.file.Files; | ||
| 20 | import java.nio.file.Path; | ||
| 21 | import java.util.concurrent.ExecutionException; | ||
| 22 | import java.util.concurrent.TimeUnit; | ||
| 23 | import java.util.function.Supplier; | ||
| 24 | |||
| 25 | public final class ClassCache implements AutoCloseable, ClassProvider { | ||
| 26 | private final FileSystem fileSystem; | ||
| 27 | private final ImmutableSet<String> classNames; | ||
| 28 | |||
| 29 | private final Cache<String, ClassNode> nodeCache = CacheBuilder.newBuilder() | ||
| 30 | .maximumSize(128) | ||
| 31 | .expireAfterAccess(1, TimeUnit.MINUTES) | ||
| 32 | .build(); | ||
| 33 | |||
| 34 | private ClassCache(FileSystem fileSystem, ImmutableSet<String> classNames) { | ||
| 35 | this.fileSystem = fileSystem; | ||
| 36 | this.classNames = classNames; | ||
| 37 | } | ||
| 38 | |||
| 39 | public static ClassCache of(Path jarPath) throws IOException { | ||
| 40 | FileSystem fileSystem = FileSystems.newFileSystem(jarPath, (ClassLoader) null); | ||
| 41 | ImmutableSet<String> classNames = collectClassNames(fileSystem); | ||
| 42 | |||
| 43 | return new ClassCache(fileSystem, classNames); | ||
| 44 | } | ||
| 45 | |||
| 46 | private static ImmutableSet<String> collectClassNames(FileSystem fileSystem) throws IOException { | ||
| 47 | ImmutableSet.Builder<String> classNames = ImmutableSet.builder(); | ||
| 48 | for (Path root : fileSystem.getRootDirectories()) { | ||
| 49 | Files.walk(root).map(Path::toString) | ||
| 50 | .forEach(path -> { | ||
| 51 | if (path.endsWith(".class")) { | ||
| 52 | String name = path.substring(1, path.length() - ".class".length()); | ||
| 53 | classNames.add(name); | ||
| 54 | } | ||
| 55 | }); | ||
| 56 | } | ||
| 57 | |||
| 58 | return classNames.build(); | ||
| 59 | } | ||
| 60 | |||
| 61 | @Nullable | ||
| 62 | @Override | ||
| 63 | public ClassNode getClassNode(String name) { | ||
| 64 | if (!classNames.contains(name)) { | ||
| 65 | return null; | ||
| 66 | } | ||
| 67 | |||
| 68 | try { | ||
| 69 | return nodeCache.get(name, () -> parseNode(name)); | ||
| 70 | } catch (ExecutionException e) { | ||
| 71 | throw new RuntimeException(e); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | private ClassNode parseNode(String name) throws IOException { | ||
| 76 | ClassReader reader = getReader(name); | ||
| 77 | |||
| 78 | ClassNode node = new ClassNode(); | ||
| 79 | |||
| 80 | LocalVariableFixVisitor visitor = new LocalVariableFixVisitor(Enigma.ASM_VERSION, node); | ||
| 81 | reader.accept(visitor, 0); | ||
| 82 | |||
| 83 | return node; | ||
| 84 | } | ||
| 85 | |||
| 86 | private ClassReader getReader(String name) throws IOException { | ||
| 87 | Path path = fileSystem.getPath(name + ".class"); | ||
| 88 | byte[] bytes = Files.readAllBytes(path); | ||
| 89 | return new ClassReader(bytes); | ||
| 90 | } | ||
| 91 | |||
| 92 | public int getClassCount() { | ||
| 93 | return classNames.size(); | ||
| 94 | } | ||
| 95 | |||
| 96 | public void visit(Supplier<ClassVisitor> visitorSupplier, int readFlags) { | ||
| 97 | for (String className : classNames) { | ||
| 98 | ClassVisitor visitor = visitorSupplier.get(); | ||
| 99 | |||
| 100 | ClassNode cached = nodeCache.getIfPresent(className); | ||
| 101 | if (cached != null) { | ||
| 102 | cached.accept(visitor); | ||
| 103 | continue; | ||
| 104 | } | ||
| 105 | |||
| 106 | try { | ||
| 107 | ClassReader reader = getReader(className); | ||
| 108 | reader.accept(visitor, readFlags); | ||
| 109 | } catch (IOException e) { | ||
| 110 | System.out.println("Failed to visit class " + className); | ||
| 111 | e.printStackTrace(); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | @Override | ||
| 117 | public void close() throws IOException { | ||
| 118 | this.fileSystem.close(); | ||
| 119 | } | ||
| 120 | |||
| 121 | public JarIndex index(ProgressListener progress) { | ||
| 122 | JarIndex index = JarIndex.empty(); | ||
| 123 | index.indexJar(this, progress); | ||
| 124 | return index; | ||
| 125 | } | ||
| 126 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java index de3f5f57..b5ad91a8 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java | |||
| @@ -15,20 +15,21 @@ import com.google.common.collect.HashMultimap; | |||
| 15 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| 16 | import cuchaz.enigma.Enigma; | 16 | import cuchaz.enigma.Enigma; |
| 17 | import cuchaz.enigma.ProgressListener; | 17 | import cuchaz.enigma.ProgressListener; |
| 18 | import cuchaz.enigma.analysis.ClassCache; | ||
| 19 | import cuchaz.enigma.analysis.ReferenceTargetType; | 18 | import cuchaz.enigma.analysis.ReferenceTargetType; |
| 19 | import cuchaz.enigma.classprovider.ClassProvider; | ||
| 20 | import cuchaz.enigma.translation.mapping.EntryResolver; | 20 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 21 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | 21 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; |
| 22 | import cuchaz.enigma.translation.representation.Lambda; | 22 | import cuchaz.enigma.translation.representation.Lambda; |
| 23 | import cuchaz.enigma.translation.representation.entry.*; | 23 | import cuchaz.enigma.translation.representation.entry.*; |
| 24 | import cuchaz.enigma.utils.I18n; | 24 | import cuchaz.enigma.utils.I18n; |
| 25 | 25 | ||
| 26 | import org.objectweb.asm.ClassReader; | ||
| 27 | |||
| 28 | import java.util.Arrays; | 26 | import java.util.Arrays; |
| 29 | import java.util.Collection; | 27 | import java.util.Collection; |
| 28 | import java.util.HashSet; | ||
| 29 | import java.util.Set; | ||
| 30 | 30 | ||
| 31 | public class JarIndex implements JarIndexer { | 31 | public class JarIndex implements JarIndexer { |
| 32 | private final Set<String> indexedClasses = new HashSet<>(); | ||
| 32 | private final EntryIndex entryIndex; | 33 | private final EntryIndex entryIndex; |
| 33 | private final InheritanceIndex inheritanceIndex; | 34 | private final InheritanceIndex inheritanceIndex; |
| 34 | private final ReferenceIndex referenceIndex; | 35 | private final ReferenceIndex referenceIndex; |
| @@ -59,14 +60,21 @@ public class JarIndex implements JarIndexer { | |||
| 59 | return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); | 60 | return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | public void indexJar(ClassCache classCache, ProgressListener progress) { | 63 | public void indexJar(Set<String> classNames, ClassProvider classProvider, ProgressListener progress) { |
| 64 | indexedClasses.addAll(classNames); | ||
| 63 | progress.init(4, I18n.translate("progress.jar.indexing")); | 65 | progress.init(4, I18n.translate("progress.jar.indexing")); |
| 64 | 66 | ||
| 65 | progress.step(1, I18n.translate("progress.jar.indexing.entries")); | 67 | progress.step(1, I18n.translate("progress.jar.indexing.entries")); |
| 66 | classCache.visit(() -> new IndexClassVisitor(this, Enigma.ASM_VERSION), ClassReader.SKIP_CODE); | 68 | |
| 69 | for (String className : classNames) { | ||
| 70 | classProvider.get(className).accept(new IndexClassVisitor(this, Enigma.ASM_VERSION)); | ||
| 71 | } | ||
| 67 | 72 | ||
| 68 | progress.step(2, I18n.translate("progress.jar.indexing.references")); | 73 | progress.step(2, I18n.translate("progress.jar.indexing.references")); |
| 69 | classCache.visit(() -> new IndexReferenceVisitor(this, entryIndex, inheritanceIndex, Enigma.ASM_VERSION), 0); | 74 | |
| 75 | for (String className : classNames) { | ||
| 76 | classProvider.get(className).accept(new IndexReferenceVisitor(this, entryIndex, inheritanceIndex, Enigma.ASM_VERSION)); | ||
| 77 | } | ||
| 70 | 78 | ||
| 71 | progress.step(3, I18n.translate("progress.jar.indexing.methods")); | 79 | progress.step(3, I18n.translate("progress.jar.indexing.methods")); |
| 72 | bridgeMethodIndex.findBridgeMethods(); | 80 | bridgeMethodIndex.findBridgeMethods(); |
| @@ -167,4 +175,8 @@ public class JarIndex implements JarIndexer { | |||
| 167 | public EntryResolver getEntryResolver() { | 175 | public EntryResolver getEntryResolver() { |
| 168 | return entryResolver; | 176 | return entryResolver; |
| 169 | } | 177 | } |
| 178 | |||
| 179 | public boolean isIndexed(String internalName) { | ||
| 180 | return indexedClasses.contains(internalName); | ||
| 181 | } | ||
| 170 | } | 182 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java b/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java index 0cda1998..5417531c 100644 --- a/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java +++ b/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java | |||
| @@ -1,10 +1,21 @@ | |||
| 1 | package cuchaz.enigma.api.service; | 1 | package cuchaz.enigma.api.service; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.analysis.ClassCache; | ||
| 4 | import cuchaz.enigma.analysis.index.JarIndex; | 3 | import cuchaz.enigma.analysis.index.JarIndex; |
| 4 | import cuchaz.enigma.classprovider.ClassProvider; | ||
| 5 | import org.objectweb.asm.ClassVisitor; | ||
| 6 | |||
| 7 | import java.util.Set; | ||
| 5 | 8 | ||
| 6 | public interface JarIndexerService extends EnigmaService { | 9 | public interface JarIndexerService extends EnigmaService { |
| 7 | EnigmaServiceType<JarIndexerService> TYPE = EnigmaServiceType.create("jar_indexer"); | 10 | EnigmaServiceType<JarIndexerService> TYPE = EnigmaServiceType.create("jar_indexer"); |
| 8 | 11 | ||
| 9 | void acceptJar(ClassCache classCache, JarIndex jarIndex); | 12 | void acceptJar(Set<String> scope, ClassProvider classProvider, JarIndex jarIndex); |
| 13 | |||
| 14 | static JarIndexerService fromVisitor(ClassVisitor visitor) { | ||
| 15 | return (scope, classProvider, jarIndex) -> { | ||
| 16 | for (String className : scope) { | ||
| 17 | classProvider.get(className).accept(visitor); | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | } | ||
| 10 | } | 21 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java index f9e4eff8..bc38e61b 100644 --- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java | |||
| @@ -11,11 +11,10 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; | |||
| 11 | 11 | ||
| 12 | import javax.annotation.Nullable; | 12 | import javax.annotation.Nullable; |
| 13 | 13 | ||
| 14 | import org.objectweb.asm.tree.ClassNode; | 14 | import cuchaz.enigma.classprovider.CachingClassProvider; |
| 15 | import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; | ||
| 15 | 16 | ||
| 16 | import cuchaz.enigma.Enigma; | ||
| 17 | import cuchaz.enigma.EnigmaProject; | 17 | import cuchaz.enigma.EnigmaProject; |
| 18 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; | ||
| 19 | import cuchaz.enigma.events.ClassHandleListener; | 18 | import cuchaz.enigma.events.ClassHandleListener; |
| 20 | import cuchaz.enigma.events.ClassHandleListener.InvalidationType; | 19 | import cuchaz.enigma.events.ClassHandleListener.InvalidationType; |
| 21 | import cuchaz.enigma.source.*; | 20 | import cuchaz.enigma.source.*; |
| @@ -89,17 +88,7 @@ public final class ClassHandleProvider { | |||
| 89 | } | 88 | } |
| 90 | 89 | ||
| 91 | private Decompiler createDecompiler() { | 90 | private Decompiler createDecompiler() { |
| 92 | return ds.create(name -> { | 91 | return ds.create(new CachingClassProvider(new ObfuscationFixClassProvider(project.getClassProvider(), project.getJarIndex())), new SourceSettings(true, true)); |
| 93 | ClassNode node = project.getClassCache().getClassNode(name); | ||
| 94 | |||
| 95 | if (node == null) { | ||
| 96 | return null; | ||
| 97 | } | ||
| 98 | |||
| 99 | ClassNode fixedNode = new ClassNode(); | ||
| 100 | node.accept(new SourceFixVisitor(Enigma.ASM_VERSION, fixedNode, project.getJarIndex())); | ||
| 101 | return fixedNode; | ||
| 102 | }, new SourceSettings(true, true)); | ||
| 103 | } | 92 | } |
| 104 | 93 | ||
| 105 | /** | 94 | /** |
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java new file mode 100644 index 00000000..47f5eb86 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | ||
| 2 | |||
| 3 | import com.google.common.cache.Cache; | ||
| 4 | import com.google.common.cache.CacheBuilder; | ||
| 5 | import org.objectweb.asm.tree.ClassNode; | ||
| 6 | |||
| 7 | import javax.annotation.Nullable; | ||
| 8 | import java.util.Optional; | ||
| 9 | import java.util.concurrent.ExecutionException; | ||
| 10 | import java.util.concurrent.TimeUnit; | ||
| 11 | |||
| 12 | /** | ||
| 13 | * Wraps a ClassProvider to provide caching and synchronization. | ||
| 14 | */ | ||
| 15 | public class CachingClassProvider implements ClassProvider { | ||
| 16 | private final ClassProvider classProvider; | ||
| 17 | private final Cache<String, Optional<ClassNode>> cache = CacheBuilder.newBuilder() | ||
| 18 | .maximumSize(128) | ||
| 19 | .expireAfterAccess(1, TimeUnit.MINUTES) | ||
| 20 | .concurrencyLevel(1) | ||
| 21 | .build(); | ||
| 22 | |||
| 23 | public CachingClassProvider(ClassProvider classProvider) { | ||
| 24 | this.classProvider = classProvider; | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | @Nullable | ||
| 29 | public ClassNode get(String name) { | ||
| 30 | try { | ||
| 31 | return cache.get(name, () -> Optional.ofNullable(classProvider.get(name))).orElse(null); | ||
| 32 | } catch (ExecutionException e) { | ||
| 33 | throw new RuntimeException(e); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java new file mode 100644 index 00000000..6e4a665a --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | ||
| 2 | |||
| 3 | import org.objectweb.asm.tree.ClassNode; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | |||
| 7 | public interface ClassProvider { | ||
| 8 | /** | ||
| 9 | * Gets the {@linkplain ClassNode} for a class. The class provider may return a cached result, | ||
| 10 | * so it's important to not mutate it. | ||
| 11 | * | ||
| 12 | * @param name the internal name of the class | ||
| 13 | * @return the {@linkplain ClassNode} for that class, or {@code null} if it was not found | ||
| 14 | */ | ||
| 15 | @Nullable | ||
| 16 | ClassNode get(String name); | ||
| 17 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java new file mode 100644 index 00000000..e9472fa9 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | ||
| 2 | |||
| 3 | import org.objectweb.asm.ClassReader; | ||
| 4 | import org.objectweb.asm.tree.ClassNode; | ||
| 5 | |||
| 6 | import javax.annotation.Nullable; | ||
| 7 | import java.io.IOException; | ||
| 8 | import java.io.InputStream; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * Provides classes by loading them from the classpath. | ||
| 12 | */ | ||
| 13 | public class ClasspathClassProvider implements ClassProvider { | ||
| 14 | @Nullable @Override public ClassNode get(String name) { | ||
| 15 | try (InputStream in = ClasspathClassProvider.class.getResourceAsStream("/" + name + ".class")) { | ||
| 16 | if (in == null) { | ||
| 17 | return null; | ||
| 18 | } | ||
| 19 | |||
| 20 | ClassNode node = new ClassNode(); | ||
| 21 | new ClassReader(in).accept(node, 0); | ||
| 22 | return node; | ||
| 23 | } catch (IOException e) { | ||
| 24 | return null; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java new file mode 100644 index 00000000..865464cf --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | ||
| 2 | |||
| 3 | import org.objectweb.asm.tree.ClassNode; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Combines a list of {@link ClassProvider}s into one, calling each one in a row | ||
| 9 | * until one can provide the class. | ||
| 10 | */ | ||
| 11 | public class CombiningClassProvider implements ClassProvider { | ||
| 12 | private final ClassProvider[] classProviders; | ||
| 13 | |||
| 14 | public CombiningClassProvider(ClassProvider... classProviders) { | ||
| 15 | this.classProviders = classProviders; | ||
| 16 | } | ||
| 17 | |||
| 18 | @Override | ||
| 19 | @Nullable | ||
| 20 | public ClassNode get(String name) { | ||
| 21 | for (ClassProvider cp : classProviders) { | ||
| 22 | ClassNode node = cp.get(name); | ||
| 23 | |||
| 24 | if (node != null) { | ||
| 25 | return node; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | return null; | ||
| 30 | } | ||
| 31 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java new file mode 100644 index 00000000..c614b0a8 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | ||
| 2 | |||
| 3 | import com.google.common.collect.ImmutableSet; | ||
| 4 | import cuchaz.enigma.utils.AsmUtil; | ||
| 5 | import org.objectweb.asm.tree.ClassNode; | ||
| 6 | |||
| 7 | import javax.annotation.Nullable; | ||
| 8 | import java.io.IOException; | ||
| 9 | import java.nio.file.FileSystem; | ||
| 10 | import java.nio.file.FileSystems; | ||
| 11 | import java.nio.file.Files; | ||
| 12 | import java.nio.file.Path; | ||
| 13 | import java.util.Set; | ||
| 14 | |||
| 15 | /** | ||
| 16 | * Provides classes by loading them from a JAR file. | ||
| 17 | */ | ||
| 18 | public class JarClassProvider implements AutoCloseable, ClassProvider { | ||
| 19 | private final FileSystem fileSystem; | ||
| 20 | private final Set<String> classNames; | ||
| 21 | |||
| 22 | public JarClassProvider(Path jarPath) throws IOException { | ||
| 23 | this.fileSystem = FileSystems.newFileSystem(jarPath, (ClassLoader) null); | ||
| 24 | this.classNames = collectClassNames(fileSystem); | ||
| 25 | } | ||
| 26 | |||
| 27 | private static ImmutableSet<String> collectClassNames(FileSystem fileSystem) throws IOException { | ||
| 28 | ImmutableSet.Builder<String> classNames = ImmutableSet.builder(); | ||
| 29 | for (Path root : fileSystem.getRootDirectories()) { | ||
| 30 | Files.walk(root).map(Path::toString) | ||
| 31 | .forEach(path -> { | ||
| 32 | if (path.endsWith(".class")) { | ||
| 33 | String name = path.substring(1, path.length() - ".class".length()); | ||
| 34 | classNames.add(name); | ||
| 35 | } | ||
| 36 | }); | ||
| 37 | } | ||
| 38 | |||
| 39 | return classNames.build(); | ||
| 40 | } | ||
| 41 | |||
| 42 | public Set<String> getClassNames() { | ||
| 43 | return classNames; | ||
| 44 | } | ||
| 45 | |||
| 46 | @Nullable | ||
| 47 | @Override | ||
| 48 | public ClassNode get(String name) { | ||
| 49 | if (!classNames.contains(name)) { | ||
| 50 | return null; | ||
| 51 | } | ||
| 52 | |||
| 53 | try { | ||
| 54 | return AsmUtil.bytesToNode(Files.readAllBytes(fileSystem.getPath(name + ".class"))); | ||
| 55 | } catch (IOException e) { | ||
| 56 | throw new RuntimeException(e); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | @Override | ||
| 61 | public void close() throws Exception { | ||
| 62 | fileSystem.close(); | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java new file mode 100644 index 00000000..db9e2d04 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | ||
| 2 | |||
| 3 | import cuchaz.enigma.Enigma; | ||
| 4 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 5 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; | ||
| 6 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; | ||
| 7 | import cuchaz.enigma.classprovider.ClassProvider; | ||
| 8 | import org.objectweb.asm.ClassVisitor; | ||
| 9 | import org.objectweb.asm.Opcodes; | ||
| 10 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 11 | import org.objectweb.asm.tree.ClassNode; | ||
| 12 | import org.objectweb.asm.tree.MethodInsnNode; | ||
| 13 | import org.objectweb.asm.tree.MethodNode; | ||
| 14 | |||
| 15 | import javax.annotation.Nullable; | ||
| 16 | |||
| 17 | /** | ||
| 18 | * Wraps a ClassProvider to apply fixes to the following problems introduced by the obfuscator, | ||
| 19 | * so that the classes can be decompiled correctly: | ||
| 20 | * <ul> | ||
| 21 | * <li>Bridge methods missing the "bridge" or "synthetic" access modifier | ||
| 22 | * <li>.getClass() calls added by Proguard (Proguard adds these to preserve semantics when Proguard removes | ||
| 23 | * a field access which may have caused a {@code NullPointerException}). | ||
| 24 | * <li>LVT names that don't match parameter table name | ||
| 25 | * <li>LVT names that are invalid or duplicate | ||
| 26 | * <li>Enum constructor parameters that are incorrectly named or missing the "synthetic" access modifier | ||
| 27 | * <li>"this" parameter which is incorrectly named | ||
| 28 | * </ul> | ||
| 29 | * <p> | ||
| 30 | * These fixes are only applied to classes that were indexed by the JarIndex provided, and not library classes. | ||
| 31 | */ | ||
| 32 | public class ObfuscationFixClassProvider implements ClassProvider { | ||
| 33 | private final ClassProvider classProvider; | ||
| 34 | private final JarIndex jarIndex; | ||
| 35 | |||
| 36 | public ObfuscationFixClassProvider(ClassProvider classProvider, JarIndex jarIndex) { | ||
| 37 | this.classProvider = classProvider; | ||
| 38 | this.jarIndex = jarIndex; | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | @Nullable | ||
| 43 | public ClassNode get(String name) { | ||
| 44 | ClassNode node = classProvider.get(name); | ||
| 45 | |||
| 46 | if (!jarIndex.isIndexed(name)) { | ||
| 47 | return node; | ||
| 48 | } | ||
| 49 | |||
| 50 | ClassNode fixedNode = new ClassNode(); | ||
| 51 | ClassVisitor visitor = fixedNode; | ||
| 52 | visitor = new LocalVariableFixVisitor(Enigma.ASM_VERSION, visitor); | ||
| 53 | visitor = new SourceFixVisitor(Enigma.ASM_VERSION, visitor, jarIndex); | ||
| 54 | node.accept(visitor); | ||
| 55 | removeRedundantClassCalls(fixedNode); | ||
| 56 | |||
| 57 | return fixedNode; | ||
| 58 | } | ||
| 59 | |||
| 60 | private void removeRedundantClassCalls(ClassNode node) { | ||
| 61 | // Removes .getClass() calls added by Proguard: | ||
| 62 | // DUP | ||
| 63 | // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; | ||
| 64 | // POP | ||
| 65 | for (MethodNode methodNode : node.methods) { | ||
| 66 | AbstractInsnNode insnNode = methodNode.instructions.getFirst(); | ||
| 67 | while (insnNode != null) { | ||
| 68 | if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) { | ||
| 69 | MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; | ||
| 70 | if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) { | ||
| 71 | AbstractInsnNode previous = methodInsnNode.getPrevious(); | ||
| 72 | AbstractInsnNode next = methodInsnNode.getNext(); | ||
| 73 | if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) { | ||
| 74 | insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction | ||
| 75 | methodNode.instructions.remove(previous); | ||
| 76 | methodNode.instructions.remove(methodInsnNode); | ||
| 77 | methodNode.instructions.remove(next); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | insnNode = insnNode.getNext(); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java b/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java index 377ccbc1..638498f7 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java +++ b/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | package cuchaz.enigma.source; | 1 | package cuchaz.enigma.source; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.ClassProvider; | 3 | import cuchaz.enigma.classprovider.ClassProvider; |
| 4 | import cuchaz.enigma.api.service.EnigmaService; | 4 | import cuchaz.enigma.api.service.EnigmaService; |
| 5 | import cuchaz.enigma.api.service.EnigmaServiceType; | 5 | import cuchaz.enigma.api.service.EnigmaServiceType; |
| 6 | 6 | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java index 9e37f168..941a699f 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | 1 | package cuchaz.enigma.source.cfr; |
| 2 | 2 | ||
| 3 | import com.google.common.io.ByteStreams; | 3 | import cuchaz.enigma.classprovider.ClassProvider; |
| 4 | import cuchaz.enigma.ClassProvider; | ||
| 5 | import cuchaz.enigma.source.Decompiler; | 4 | import cuchaz.enigma.source.Decompiler; |
| 6 | import cuchaz.enigma.source.Source; | 5 | import cuchaz.enigma.source.Source; |
| 7 | import cuchaz.enigma.source.SourceSettings; | 6 | import cuchaz.enigma.source.SourceSettings; |
| 7 | import cuchaz.enigma.utils.AsmUtil; | ||
| 8 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; | 8 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; |
| 9 | import org.benf.cfr.reader.apiunreleased.JarContent; | 9 | import org.benf.cfr.reader.apiunreleased.JarContent; |
| 10 | import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; | 10 | import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; |
| @@ -19,16 +19,12 @@ import org.benf.cfr.reader.util.CannotLoadClassException; | |||
| 19 | import org.benf.cfr.reader.util.collections.ListFactory; | 19 | import org.benf.cfr.reader.util.collections.ListFactory; |
| 20 | import org.benf.cfr.reader.util.getopt.Options; | 20 | import org.benf.cfr.reader.util.getopt.Options; |
| 21 | import org.benf.cfr.reader.util.getopt.OptionsImpl; | 21 | import org.benf.cfr.reader.util.getopt.OptionsImpl; |
| 22 | import org.objectweb.asm.ClassWriter; | ||
| 23 | import org.objectweb.asm.tree.ClassNode; | 22 | import org.objectweb.asm.tree.ClassNode; |
| 24 | 23 | ||
| 25 | import java.io.IOException; | ||
| 26 | import java.io.InputStream; | ||
| 27 | import java.util.Collection; | 24 | import java.util.Collection; |
| 28 | import java.util.HashMap; | 25 | import java.util.HashMap; |
| 29 | import java.util.Map; | 26 | import java.util.Map; |
| 30 | 27 | ||
| 31 | |||
| 32 | public class CfrDecompiler implements Decompiler { | 28 | public class CfrDecompiler implements Decompiler { |
| 33 | private final DCCommonState state; | 29 | private final DCCommonState state; |
| 34 | 30 | ||
| @@ -58,21 +54,13 @@ public class CfrDecompiler implements Decompiler { | |||
| 58 | 54 | ||
| 59 | @Override | 55 | @Override |
| 60 | public Pair<byte[], String> getClassFileContent(String path) { | 56 | public Pair<byte[], String> getClassFileContent(String path) { |
| 61 | ClassNode node = classProvider.getClassNode(path.substring(0, path.lastIndexOf('.'))); | 57 | ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); |
| 62 | 58 | ||
| 63 | if (node == null) { | 59 | 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; | 60 | return null; |
| 71 | } | 61 | } |
| 72 | 62 | ||
| 73 | ClassWriter cw = new ClassWriter(0); | 63 | return new Pair<>(AsmUtil.nodeToBytes(node), path); |
| 74 | node.accept(cw); | ||
| 75 | return new Pair<>(cw.toByteArray(), path); | ||
| 76 | } | 64 | } |
| 77 | }); | 65 | }); |
| 78 | } | 66 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java index 3cbd680c..9dc1e0a8 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java | |||
| @@ -11,14 +11,13 @@ import com.strobel.decompiler.languages.java.JavaFormattingOptions; | |||
| 11 | import com.strobel.decompiler.languages.java.ast.AstBuilder; | 11 | import com.strobel.decompiler.languages.java.ast.AstBuilder; |
| 12 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 12 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 13 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; | 13 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; |
| 14 | import cuchaz.enigma.ClassProvider; | 14 | import cuchaz.enigma.classprovider.ClassProvider; |
| 15 | import cuchaz.enigma.source.Source; | 15 | import cuchaz.enigma.source.Source; |
| 16 | import cuchaz.enigma.source.Decompiler; | 16 | import cuchaz.enigma.source.Decompiler; |
| 17 | import cuchaz.enigma.source.SourceSettings; | 17 | import cuchaz.enigma.source.SourceSettings; |
| 18 | import cuchaz.enigma.source.procyon.transformers.*; | 18 | import cuchaz.enigma.source.procyon.transformers.*; |
| 19 | import cuchaz.enigma.source.procyon.typeloader.CompiledSourceTypeLoader; | 19 | import cuchaz.enigma.utils.AsmUtil; |
| 20 | import cuchaz.enigma.source.procyon.typeloader.NoRetryMetadataSystem; | 20 | import org.objectweb.asm.tree.ClassNode; |
| 21 | import cuchaz.enigma.source.procyon.typeloader.SynchronizedTypeLoader; | ||
| 22 | 21 | ||
| 23 | public class ProcyonDecompiler implements Decompiler { | 22 | public class ProcyonDecompiler implements Decompiler { |
| 24 | private final SourceSettings settings; | 23 | private final SourceSettings settings; |
| @@ -26,9 +25,21 @@ public class ProcyonDecompiler implements Decompiler { | |||
| 26 | private final MetadataSystem metadataSystem; | 25 | private final MetadataSystem metadataSystem; |
| 27 | 26 | ||
| 28 | public ProcyonDecompiler(ClassProvider classProvider, SourceSettings settings) { | 27 | public ProcyonDecompiler(ClassProvider classProvider, SourceSettings settings) { |
| 29 | ITypeLoader typeLoader = new SynchronizedTypeLoader(new CompiledSourceTypeLoader(classProvider)); | 28 | ITypeLoader typeLoader = (name, buffer) -> { |
| 29 | ClassNode node = classProvider.get(name); | ||
| 30 | 30 | ||
| 31 | metadataSystem = new NoRetryMetadataSystem(typeLoader); | 31 | if (node == null) { |
| 32 | return false; | ||
| 33 | } | ||
| 34 | |||
| 35 | byte[] data = AsmUtil.nodeToBytes(node); | ||
| 36 | buffer.reset(data.length); | ||
| 37 | System.arraycopy(data, 0, buffer.array(), buffer.position(), data.length); | ||
| 38 | buffer.position(0); | ||
| 39 | return true; | ||
| 40 | }; | ||
| 41 | |||
| 42 | metadataSystem = new MetadataSystem(typeLoader); | ||
| 32 | metadataSystem.setEagerMethodLoadingEnabled(true); | 43 | metadataSystem.setEagerMethodLoadingEnabled(true); |
| 33 | 44 | ||
| 34 | decompilerSettings = DecompilerSettings.javaDefaults(); | 45 | decompilerSettings = DecompilerSettings.javaDefaults(); |
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java deleted file mode 100644 index e702956e..00000000 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingClasspathTypeLoader.java +++ /dev/null | |||
| @@ -1,33 +0,0 @@ | |||
| 1 | package cuchaz.enigma.source.procyon.typeloader; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.Buffer; | ||
| 4 | import com.strobel.assembler.metadata.ClasspathTypeLoader; | ||
| 5 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Caching version of {@link ClasspathTypeLoader} | ||
| 9 | */ | ||
| 10 | public class CachingClasspathTypeLoader extends CachingTypeLoader { | ||
| 11 | private static ITypeLoader extraClassPathLoader = null; | ||
| 12 | |||
| 13 | public static void setExtraClassPathLoader(ITypeLoader loader){ | ||
| 14 | extraClassPathLoader = loader; | ||
| 15 | } | ||
| 16 | |||
| 17 | private final ITypeLoader classpathLoader = new ClasspathTypeLoader(); | ||
| 18 | |||
| 19 | @Override | ||
| 20 | protected byte[] doLoad(String className) { | ||
| 21 | Buffer parentBuf = new Buffer(); | ||
| 22 | if (classpathLoader.tryLoadType(className, parentBuf)) { | ||
| 23 | return parentBuf.array(); | ||
| 24 | } | ||
| 25 | if (extraClassPathLoader != null){ | ||
| 26 | parentBuf.reset(); | ||
| 27 | if (extraClassPathLoader.tryLoadType(className, parentBuf)){ | ||
| 28 | return parentBuf.array(); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | return EMPTY_ARRAY;//need to return *something* as null means no store | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java deleted file mode 100644 index 5be5ddd9..00000000 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CachingTypeLoader.java +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | package cuchaz.enigma.source.procyon.typeloader; | ||
| 2 | |||
| 3 | import com.google.common.collect.Maps; | ||
| 4 | import com.strobel.assembler.metadata.Buffer; | ||
| 5 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 6 | |||
| 7 | import java.util.Map; | ||
| 8 | |||
| 9 | /** | ||
| 10 | * Common cache functions | ||
| 11 | */ | ||
| 12 | public abstract class CachingTypeLoader implements ITypeLoader { | ||
| 13 | protected static final byte[] EMPTY_ARRAY = {}; | ||
| 14 | |||
| 15 | private final Map<String, byte[]> cache = Maps.newHashMap(); | ||
| 16 | |||
| 17 | protected abstract byte[] doLoad(String className); | ||
| 18 | |||
| 19 | @Override | ||
| 20 | public boolean tryLoadType(String className, Buffer out) { | ||
| 21 | |||
| 22 | // check the cache | ||
| 23 | byte[] data = this.cache.computeIfAbsent(className, this::doLoad); | ||
| 24 | |||
| 25 | if (data == EMPTY_ARRAY) { | ||
| 26 | return false; | ||
| 27 | } | ||
| 28 | |||
| 29 | out.reset(data.length); | ||
| 30 | System.arraycopy(data, 0, out.array(), out.position(), data.length); | ||
| 31 | out.position(0); | ||
| 32 | return true; | ||
| 33 | } | ||
| 34 | |||
| 35 | public void clearCache() { | ||
| 36 | this.cache.clear(); | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java deleted file mode 100644 index e703d3b3..00000000 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/CompiledSourceTypeLoader.java +++ /dev/null | |||
| @@ -1,140 +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.source.procyon.typeloader; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.strobel.assembler.metadata.Buffer; | ||
| 16 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 17 | import cuchaz.enigma.ClassProvider; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | import org.objectweb.asm.ClassVisitor; | ||
| 20 | import org.objectweb.asm.ClassWriter; | ||
| 21 | import org.objectweb.asm.Opcodes; | ||
| 22 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 23 | import org.objectweb.asm.tree.ClassNode; | ||
| 24 | import org.objectweb.asm.tree.MethodInsnNode; | ||
| 25 | import org.objectweb.asm.tree.MethodNode; | ||
| 26 | |||
| 27 | import java.util.Collection; | ||
| 28 | import java.util.LinkedList; | ||
| 29 | import java.util.List; | ||
| 30 | import java.util.function.Function; | ||
| 31 | |||
| 32 | public class CompiledSourceTypeLoader extends CachingTypeLoader { | ||
| 33 | //Store one instance as the classpath shouldn't change during load | ||
| 34 | private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader(); | ||
| 35 | |||
| 36 | private final ClassProvider compiledSource; | ||
| 37 | private final LinkedList<Function<ClassVisitor, ClassVisitor>> visitors = new LinkedList<>(); | ||
| 38 | |||
| 39 | public CompiledSourceTypeLoader(ClassProvider compiledSource) { | ||
| 40 | this.compiledSource = compiledSource; | ||
| 41 | } | ||
| 42 | |||
| 43 | public void addVisitor(Function<ClassVisitor, ClassVisitor> visitor) { | ||
| 44 | this.visitors.addFirst(visitor); | ||
| 45 | } | ||
| 46 | |||
| 47 | @Override | ||
| 48 | protected byte[] doLoad(String className) { | ||
| 49 | byte[] data = loadType(className); | ||
| 50 | if (data == null) { | ||
| 51 | return loadClasspath(className); | ||
| 52 | } | ||
| 53 | |||
| 54 | return data; | ||
| 55 | } | ||
| 56 | |||
| 57 | private byte[] loadClasspath(String name) { | ||
| 58 | Buffer parentBuf = new Buffer(); | ||
| 59 | if (CLASSPATH_TYPE_LOADER.tryLoadType(name, parentBuf)) { | ||
| 60 | return parentBuf.array(); | ||
| 61 | } | ||
| 62 | return EMPTY_ARRAY; | ||
| 63 | } | ||
| 64 | |||
| 65 | private byte[] loadType(String className) { | ||
| 66 | ClassEntry entry = new ClassEntry(className); | ||
| 67 | |||
| 68 | // find the class in the jar | ||
| 69 | ClassNode node = findClassNode(entry); | ||
| 70 | if (node == null) { | ||
| 71 | // couldn't find it | ||
| 72 | return null; | ||
| 73 | } | ||
| 74 | |||
| 75 | removeRedundantClassCalls(node); | ||
| 76 | |||
| 77 | ClassWriter writer = new ClassWriter(0); | ||
| 78 | |||
| 79 | ClassVisitor visitor = writer; | ||
| 80 | for (Function<ClassVisitor, ClassVisitor> visitorFunction : this.visitors) { | ||
| 81 | visitor = visitorFunction.apply(visitor); | ||
| 82 | } | ||
| 83 | |||
| 84 | node.accept(visitor); | ||
| 85 | |||
| 86 | // we have a transformed class! | ||
| 87 | return writer.toByteArray(); | ||
| 88 | } | ||
| 89 | |||
| 90 | private void removeRedundantClassCalls(ClassNode node) { | ||
| 91 | // remove <obj>.getClass() calls that are seemingly injected | ||
| 92 | // DUP | ||
| 93 | // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; | ||
| 94 | // POP | ||
| 95 | for (MethodNode methodNode : node.methods) { | ||
| 96 | AbstractInsnNode insnNode = methodNode.instructions.getFirst(); | ||
| 97 | while (insnNode != null) { | ||
| 98 | if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) { | ||
| 99 | MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; | ||
| 100 | if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) { | ||
| 101 | AbstractInsnNode previous = methodInsnNode.getPrevious(); | ||
| 102 | AbstractInsnNode next = methodInsnNode.getNext(); | ||
| 103 | if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) { | ||
| 104 | insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction | ||
| 105 | methodNode.instructions.remove(previous); | ||
| 106 | methodNode.instructions.remove(methodInsnNode); | ||
| 107 | methodNode.instructions.remove(next); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | insnNode = insnNode.getNext(); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | private ClassNode findClassNode(ClassEntry entry) { | ||
| 117 | // try to find the class in the jar | ||
| 118 | for (String className : getClassNamesToTry(entry)) { | ||
| 119 | ClassNode node = compiledSource.getClassNode(className); | ||
| 120 | if (node != null) { | ||
| 121 | return node; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | // didn't find it ;_; | ||
| 126 | return null; | ||
| 127 | } | ||
| 128 | |||
| 129 | private Collection<String> getClassNamesToTry(ClassEntry entry) { | ||
| 130 | List<String> classNamesToTry = Lists.newArrayList(); | ||
| 131 | classNamesToTry.add(entry.getFullName()); | ||
| 132 | |||
| 133 | ClassEntry outerClass = entry.getOuterClass(); | ||
| 134 | if (outerClass != null) { | ||
| 135 | classNamesToTry.addAll(getClassNamesToTry(outerClass)); | ||
| 136 | } | ||
| 137 | |||
| 138 | return classNamesToTry; | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java deleted file mode 100644 index c4732b04..00000000 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/NoRetryMetadataSystem.java +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | package cuchaz.enigma.source.procyon.typeloader; | ||
| 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 | |||
| 8 | import java.util.Collections; | ||
| 9 | import java.util.Set; | ||
| 10 | import java.util.concurrent.ConcurrentHashMap; | ||
| 11 | |||
| 12 | public final class NoRetryMetadataSystem extends MetadataSystem { | ||
| 13 | private final Set<String> failedTypes = Collections.newSetFromMap(new ConcurrentHashMap<>()); | ||
| 14 | |||
| 15 | public NoRetryMetadataSystem(final ITypeLoader typeLoader) { | ||
| 16 | super(typeLoader); | ||
| 17 | } | ||
| 18 | |||
| 19 | @Override | ||
| 20 | protected synchronized TypeDefinition resolveType(final String descriptor, final boolean mightBePrimitive) { | ||
| 21 | if (failedTypes.contains(descriptor)) { | ||
| 22 | return null; | ||
| 23 | } | ||
| 24 | |||
| 25 | final TypeDefinition result = super.resolveType(descriptor, mightBePrimitive); | ||
| 26 | |||
| 27 | if (result == null) { | ||
| 28 | failedTypes.add(descriptor); | ||
| 29 | } | ||
| 30 | |||
| 31 | return result; | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public synchronized TypeDefinition resolve(final TypeReference type) { | ||
| 36 | return super.resolve(type); | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java deleted file mode 100644 index 86c6ecc6..00000000 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/typeloader/SynchronizedTypeLoader.java +++ /dev/null | |||
| @@ -1,20 +0,0 @@ | |||
| 1 | package cuchaz.enigma.source.procyon.typeloader; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.Buffer; | ||
| 4 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 5 | |||
| 6 | /** | ||
| 7 | * Typeloader with synchronized tryLoadType method | ||
| 8 | */ | ||
| 9 | public class SynchronizedTypeLoader implements ITypeLoader { | ||
| 10 | private final ITypeLoader delegate; | ||
| 11 | |||
| 12 | public SynchronizedTypeLoader(ITypeLoader delegate) { | ||
| 13 | this.delegate = delegate; | ||
| 14 | } | ||
| 15 | |||
| 16 | @Override | ||
| 17 | public synchronized boolean tryLoadType(String internalName, Buffer buffer) { | ||
| 18 | return delegate.tryLoadType(internalName, buffer); | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java b/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java new file mode 100644 index 00000000..7d34b020 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | package cuchaz.enigma.utils; | ||
| 2 | |||
| 3 | import org.objectweb.asm.ClassReader; | ||
| 4 | import org.objectweb.asm.ClassWriter; | ||
| 5 | import org.objectweb.asm.tree.ClassNode; | ||
| 6 | |||
| 7 | public class AsmUtil { | ||
| 8 | public static byte[] nodeToBytes(ClassNode node) { | ||
| 9 | ClassWriter w = new ClassWriter(0); | ||
| 10 | node.accept(w); | ||
| 11 | return w.toByteArray(); | ||
| 12 | } | ||
| 13 | |||
| 14 | public static ClassNode bytesToNode(byte[] bytes) { | ||
| 15 | ClassReader r = new ClassReader(bytes); | ||
| 16 | ClassNode node = new ClassNode(); | ||
| 17 | r.accept(node, 0); | ||
| 18 | return node; | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java b/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java index 1dc9748b..12515358 100644 --- a/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java +++ b/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java | |||
| @@ -11,12 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.ClassCache; | ||
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | 14 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.analysis.index.PackageVisibilityIndex; | 15 | import cuchaz.enigma.analysis.index.PackageVisibilityIndex; |
| 16 | import cuchaz.enigma.classprovider.JarClassProvider; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 18 | import org.junit.Test; | 18 | import org.junit.Test; |
| 19 | 19 | ||
| 20 | import java.nio.file.Path; | ||
| 20 | import java.nio.file.Paths; | 21 | import java.nio.file.Paths; |
| 21 | 22 | ||
| 22 | import static cuchaz.enigma.TestEntryFactory.newClass; | 23 | import static cuchaz.enigma.TestEntryFactory.newClass; |
| @@ -25,7 +26,7 @@ import static org.hamcrest.Matchers.contains; | |||
| 25 | import static org.hamcrest.Matchers.containsInAnyOrder; | 26 | import static org.hamcrest.Matchers.containsInAnyOrder; |
| 26 | 27 | ||
| 27 | public class PackageVisibilityIndexTest { | 28 | public class PackageVisibilityIndexTest { |
| 28 | 29 | public static final Path JAR = Paths.get("build/test-obf/packageAccess.jar"); | |
| 29 | private static final ClassEntry KEEP = newClass("cuchaz/enigma/inputs/Keep"); | 30 | private static final ClassEntry KEEP = newClass("cuchaz/enigma/inputs/Keep"); |
| 30 | private static final ClassEntry BASE = newClass("a"); | 31 | private static final ClassEntry BASE = newClass("a"); |
| 31 | private static final ClassEntry SAME_PACKAGE_CHILD = newClass("b"); | 32 | private static final ClassEntry SAME_PACKAGE_CHILD = newClass("b"); |
| @@ -35,8 +36,9 @@ public class PackageVisibilityIndexTest { | |||
| 35 | private final JarIndex jarIndex; | 36 | private final JarIndex jarIndex; |
| 36 | 37 | ||
| 37 | public PackageVisibilityIndexTest() throws Exception { | 38 | public PackageVisibilityIndexTest() throws Exception { |
| 38 | ClassCache classCache = ClassCache.of(Paths.get("build/test-obf/packageAccess.jar")); | 39 | JarClassProvider jcp = new JarClassProvider(JAR); |
| 39 | jarIndex = classCache.index(ProgressListener.none()); | 40 | jarIndex = JarIndex.empty(); |
| 41 | jarIndex.indexJar(jcp.getClassNames(), jcp, ProgressListener.none()); | ||
| 40 | } | 42 | } |
| 41 | 43 | ||
| 42 | @Test | 44 | @Test |
diff --git a/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java b/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java index 494d959e..6daa2931 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java +++ b/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java | |||
| @@ -11,8 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.ClassCache; | 14 | import cuchaz.enigma.classprovider.ClasspathClassProvider; |
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 16 | import cuchaz.enigma.source.Decompiler; | 15 | import cuchaz.enigma.source.Decompiler; |
| 17 | import cuchaz.enigma.source.Decompilers; | 16 | import cuchaz.enigma.source.Decompilers; |
| 18 | import cuchaz.enigma.source.SourceSettings; | 17 | import cuchaz.enigma.source.SourceSettings; |
| @@ -28,27 +27,24 @@ import static org.hamcrest.MatcherAssert.assertThat; | |||
| 28 | import static org.hamcrest.Matchers.containsInAnyOrder; | 27 | import static org.hamcrest.Matchers.containsInAnyOrder; |
| 29 | 28 | ||
| 30 | public class TestDeobfed { | 29 | public class TestDeobfed { |
| 31 | private static Enigma enigma; | 30 | public static final Path OBF = Paths.get("build/test-obf/translation.jar"); |
| 32 | private static ClassCache classCache; | 31 | public static final Path DEOBF = Paths.get("build/test-deobf/translation.jar"); |
| 33 | private static JarIndex index; | 32 | private static EnigmaProject deobfProject; |
| 34 | 33 | ||
| 35 | @BeforeClass | 34 | @BeforeClass |
| 36 | public static void beforeClass() throws Exception { | 35 | public static void beforeClass() throws Exception { |
| 37 | enigma = Enigma.create(); | 36 | Enigma enigma = Enigma.create(); |
| 38 | 37 | ||
| 39 | Path obf = Paths.get("build/test-obf/translation.jar"); | 38 | Files.createDirectories(DEOBF.getParent()); |
| 40 | Path deobf = Paths.get("build/test-deobf/translation.jar"); | 39 | EnigmaProject obfProject = enigma.openJar(OBF, new ClasspathClassProvider(), ProgressListener.none()); |
| 41 | Files.createDirectories(deobf.getParent()); | 40 | obfProject.exportRemappedJar(ProgressListener.none()).write(DEOBF, ProgressListener.none()); |
| 42 | EnigmaProject project = enigma.openJar(obf, ProgressListener.none()); | ||
| 43 | project.exportRemappedJar(ProgressListener.none()).write(deobf, ProgressListener.none()); | ||
| 44 | 41 | ||
| 45 | classCache = ClassCache.of(deobf); | 42 | deobfProject = enigma.openJar(DEOBF, new ClasspathClassProvider(), ProgressListener.none()); |
| 46 | index = classCache.index(ProgressListener.none()); | ||
| 47 | } | 43 | } |
| 48 | 44 | ||
| 49 | @Test | 45 | @Test |
| 50 | public void obfEntries() { | 46 | public void obfEntries() { |
| 51 | assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder( | 47 | assertThat(deobfProject.getJarIndex().getEntryIndex().getClasses(), containsInAnyOrder( |
| 52 | newClass("cuchaz/enigma/inputs/Keep"), | 48 | newClass("cuchaz/enigma/inputs/Keep"), |
| 53 | newClass("a"), | 49 | newClass("a"), |
| 54 | newClass("b"), | 50 | newClass("b"), |
| @@ -77,8 +73,7 @@ public class TestDeobfed { | |||
| 77 | 73 | ||
| 78 | @Test | 74 | @Test |
| 79 | public void decompile() { | 75 | public void decompile() { |
| 80 | EnigmaProject project = new EnigmaProject(enigma, classCache, index, new byte[20]); | 76 | Decompiler decompiler = Decompilers.PROCYON.create(deobfProject.getClassProvider(), new SourceSettings(false, false)); |
| 81 | Decompiler decompiler = Decompilers.PROCYON.create(project.getClassCache(), new SourceSettings(false, false)); | ||
| 82 | 77 | ||
| 83 | decompiler.getSource("a"); | 78 | decompiler.getSource("a"); |
| 84 | decompiler.getSource("b"); | 79 | decompiler.getSource("b"); |
diff --git a/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java index 6619d26e..ae42235e 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java +++ b/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
| 14 | import cuchaz.enigma.source.Decompiler; | 15 | import cuchaz.enigma.source.Decompiler; |
| 15 | import cuchaz.enigma.source.Decompilers; | 16 | import cuchaz.enigma.source.Decompilers; |
| 16 | import cuchaz.enigma.source.SourceSettings; | 17 | import cuchaz.enigma.source.SourceSettings; |
| @@ -22,7 +23,7 @@ import java.nio.file.Paths; | |||
| 22 | public class TestDeobfuscator { | 23 | public class TestDeobfuscator { |
| 23 | private EnigmaProject openProject() throws IOException { | 24 | private EnigmaProject openProject() throws IOException { |
| 24 | Enigma enigma = Enigma.create(); | 25 | Enigma enigma = Enigma.create(); |
| 25 | return enigma.openJar(Paths.get("build/test-obf/loneClass.jar"), ProgressListener.none()); | 26 | return enigma.openJar(Paths.get("build/test-obf/loneClass.jar"), new ClasspathClassProvider(), ProgressListener.none()); |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | @Test | 29 | @Test |
| @@ -34,7 +35,7 @@ public class TestDeobfuscator { | |||
| 34 | @Test | 35 | @Test |
| 35 | public void decompileClass() throws Exception { | 36 | public void decompileClass() throws Exception { |
| 36 | EnigmaProject project = openProject(); | 37 | EnigmaProject project = openProject(); |
| 37 | Decompiler decompiler = Decompilers.PROCYON.create(project.getClassCache(), new SourceSettings(false, false)); | 38 | Decompiler decompiler = Decompilers.PROCYON.create(project.getClassProvider(), new SourceSettings(false, false)); |
| 38 | 39 | ||
| 39 | decompiler.getSource("a").asString(); | 40 | decompiler.getSource("a").asString(); |
| 40 | } | 41 | } |
diff --git a/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java b/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java index 85c72f81..6b609941 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java +++ b/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java | |||
| @@ -11,14 +11,16 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.ClassCache; | ||
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | 14 | import cuchaz.enigma.analysis.index.JarIndex; |
| 15 | import cuchaz.enigma.classprovider.CachingClassProvider; | ||
| 16 | import cuchaz.enigma.classprovider.JarClassProvider; | ||
| 16 | import cuchaz.enigma.source.Decompiler; | 17 | import cuchaz.enigma.source.Decompiler; |
| 17 | import cuchaz.enigma.source.Decompilers; | 18 | import cuchaz.enigma.source.Decompilers; |
| 18 | import cuchaz.enigma.source.SourceSettings; | 19 | import cuchaz.enigma.source.SourceSettings; |
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 20 | import org.junit.Test; | 21 | import org.junit.Test; |
| 21 | 22 | ||
| 23 | import java.nio.file.Path; | ||
| 22 | import java.nio.file.Paths; | 24 | import java.nio.file.Paths; |
| 23 | 25 | ||
| 24 | import static cuchaz.enigma.TestEntryFactory.newClass; | 26 | import static cuchaz.enigma.TestEntryFactory.newClass; |
| @@ -35,13 +37,16 @@ public class TestInnerClasses { | |||
| 35 | private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); | 37 | private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); |
| 36 | private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); | 38 | private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); |
| 37 | private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a"); | 39 | private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a"); |
| 40 | public static final Path JAR = Paths.get("build/test-obf/innerClasses.jar"); | ||
| 38 | private final JarIndex index; | 41 | private final JarIndex index; |
| 39 | private final Decompiler decompiler; | 42 | private final Decompiler decompiler; |
| 40 | 43 | ||
| 41 | public TestInnerClasses() throws Exception { | 44 | public TestInnerClasses() throws Exception { |
| 42 | ClassCache classCache = ClassCache.of(Paths.get("build/test-obf/innerClasses.jar")); | 45 | JarClassProvider jcp = new JarClassProvider(JAR); |
| 43 | index = classCache.index(ProgressListener.none()); | 46 | CachingClassProvider classProvider = new CachingClassProvider(jcp); |
| 44 | decompiler = Decompilers.PROCYON.create(classCache, new SourceSettings(false, false)); | 47 | index = JarIndex.empty(); |
| 48 | index.indexJar(jcp.getClassNames(), classProvider, ProgressListener.none()); | ||
| 49 | decompiler = Decompilers.PROCYON.create(classProvider, new SourceSettings(false, false)); | ||
| 45 | } | 50 | } |
| 46 | 51 | ||
| 47 | @Test | 52 | @Test |
diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java index 48975c82..0790193d 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java | |||
| @@ -11,14 +11,16 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.ClassCache; | ||
| 15 | import cuchaz.enigma.analysis.EntryReference; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 16 | import cuchaz.enigma.analysis.index.JarIndex; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.classprovider.CachingClassProvider; | ||
| 17 | import cuchaz.enigma.classprovider.JarClassProvider; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 18 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | 19 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; |
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 20 | import org.junit.Test; | 21 | import org.junit.Test; |
| 21 | 22 | ||
| 23 | import java.nio.file.Path; | ||
| 22 | import java.nio.file.Paths; | 24 | import java.nio.file.Paths; |
| 23 | import java.util.Collection; | 25 | import java.util.Collection; |
| 24 | 26 | ||
| @@ -28,6 +30,7 @@ import static org.hamcrest.Matchers.*; | |||
| 28 | 30 | ||
| 29 | public class TestJarIndexConstructorReferences { | 31 | public class TestJarIndexConstructorReferences { |
| 30 | 32 | ||
| 33 | public static final Path JAR = Paths.get("build/test-obf/constructors.jar"); | ||
| 31 | private JarIndex index; | 34 | private JarIndex index; |
| 32 | 35 | ||
| 33 | private ClassEntry baseClass = newClass("a"); | 36 | private ClassEntry baseClass = newClass("a"); |
| @@ -37,8 +40,9 @@ public class TestJarIndexConstructorReferences { | |||
| 37 | private ClassEntry callerClass = newClass("b"); | 40 | private ClassEntry callerClass = newClass("b"); |
| 38 | 41 | ||
| 39 | public TestJarIndexConstructorReferences() throws Exception { | 42 | public TestJarIndexConstructorReferences() throws Exception { |
| 40 | ClassCache classCache = ClassCache.of(Paths.get("build/test-obf/constructors.jar")); | 43 | JarClassProvider jcp = new JarClassProvider(JAR); |
| 41 | index = classCache.index(ProgressListener.none()); | 44 | index = JarIndex.empty(); |
| 45 | index.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none()); | ||
| 42 | } | 46 | } |
| 43 | 47 | ||
| 44 | @Test | 48 | @Test |
diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java index 76e379c3..a9045f9c 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java | |||
| @@ -11,11 +11,12 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.ClassCache; | ||
| 15 | import cuchaz.enigma.analysis.EntryReference; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 16 | import cuchaz.enigma.analysis.index.EntryIndex; | 15 | import cuchaz.enigma.analysis.index.EntryIndex; |
| 17 | import cuchaz.enigma.analysis.index.InheritanceIndex; | 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 18 | import cuchaz.enigma.analysis.index.JarIndex; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.classprovider.CachingClassProvider; | ||
| 19 | import cuchaz.enigma.classprovider.JarClassProvider; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | 20 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 20 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | 21 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; |
| 21 | import cuchaz.enigma.translation.representation.AccessFlags; | 22 | import cuchaz.enigma.translation.representation.AccessFlags; |
| @@ -26,6 +27,7 @@ import cuchaz.enigma.translation.representation.entry.MethodEntry; | |||
| 26 | import org.junit.Test; | 27 | import org.junit.Test; |
| 27 | import org.objectweb.asm.Opcodes; | 28 | import org.objectweb.asm.Opcodes; |
| 28 | 29 | ||
| 30 | import java.nio.file.Path; | ||
| 29 | import java.nio.file.Paths; | 31 | import java.nio.file.Paths; |
| 30 | import java.util.Collection; | 32 | import java.util.Collection; |
| 31 | 33 | ||
| @@ -35,6 +37,7 @@ import static org.hamcrest.Matchers.*; | |||
| 35 | 37 | ||
| 36 | public class TestJarIndexInheritanceTree { | 38 | public class TestJarIndexInheritanceTree { |
| 37 | 39 | ||
| 40 | public static final Path JAR = Paths.get("build/test-obf/inheritanceTree.jar"); | ||
| 38 | private JarIndex index; | 41 | private JarIndex index; |
| 39 | 42 | ||
| 40 | private ClassEntry baseClass = newClass("a"); | 43 | private ClassEntry baseClass = newClass("a"); |
| @@ -44,10 +47,10 @@ public class TestJarIndexInheritanceTree { | |||
| 44 | private FieldEntry nameField = newField(baseClass, "a", "Ljava/lang/String;"); | 47 | private FieldEntry nameField = newField(baseClass, "a", "Ljava/lang/String;"); |
| 45 | private FieldEntry numThingsField = newField(subClassB, "a", "I"); | 48 | private FieldEntry numThingsField = newField(subClassB, "a", "I"); |
| 46 | 49 | ||
| 47 | public TestJarIndexInheritanceTree() | 50 | public TestJarIndexInheritanceTree() throws Exception { |
| 48 | throws Exception { | 51 | JarClassProvider jcp = new JarClassProvider(JAR); |
| 49 | ClassCache classCache = ClassCache.of(Paths.get("build/test-obf/inheritanceTree.jar")); | 52 | index = JarIndex.empty(); |
| 50 | index = classCache.index(ProgressListener.none()); | 53 | index.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none()); |
| 51 | } | 54 | } |
| 52 | 55 | ||
| 53 | @Test | 56 | @Test |
diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java index 103c366b..6e3755ca 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java | |||
| @@ -15,6 +15,8 @@ import cuchaz.enigma.analysis.*; | |||
| 15 | import cuchaz.enigma.analysis.index.EntryIndex; | 15 | import cuchaz.enigma.analysis.index.EntryIndex; |
| 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; | 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 17 | import cuchaz.enigma.analysis.index.JarIndex; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.classprovider.CachingClassProvider; | ||
| 19 | import cuchaz.enigma.classprovider.JarClassProvider; | ||
| 18 | import cuchaz.enigma.translation.VoidTranslator; | 20 | import cuchaz.enigma.translation.VoidTranslator; |
| 19 | import cuchaz.enigma.translation.representation.AccessFlags; | 21 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 22 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| @@ -23,6 +25,7 @@ import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | |||
| 23 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 25 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 24 | import org.junit.Test; | 26 | import org.junit.Test; |
| 25 | 27 | ||
| 28 | import java.nio.file.Path; | ||
| 26 | import java.nio.file.Paths; | 29 | import java.nio.file.Paths; |
| 27 | import java.util.Collection; | 30 | import java.util.Collection; |
| 28 | import java.util.List; | 31 | import java.util.List; |
| @@ -33,11 +36,13 @@ import static org.hamcrest.Matchers.*; | |||
| 33 | 36 | ||
| 34 | public class TestJarIndexLoneClass { | 37 | public class TestJarIndexLoneClass { |
| 35 | 38 | ||
| 39 | public static final Path JAR = Paths.get("build/test-obf/loneClass.jar"); | ||
| 36 | private JarIndex index; | 40 | private JarIndex index; |
| 37 | 41 | ||
| 38 | public TestJarIndexLoneClass() throws Exception { | 42 | public TestJarIndexLoneClass() throws Exception { |
| 39 | ClassCache classCache = ClassCache.of(Paths.get("build/test-obf/loneClass.jar")); | 43 | JarClassProvider jcp = new JarClassProvider(JAR); |
| 40 | index = classCache.index(ProgressListener.none()); | 44 | index = JarIndex.empty(); |
| 45 | index.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none()); | ||
| 41 | } | 46 | } |
| 42 | 47 | ||
| 43 | @Test | 48 | @Test |
diff --git a/enigma/src/test/java/cuchaz/enigma/TokenChecker.java b/enigma/src/test/java/cuchaz/enigma/TokenChecker.java index 96fc6dab..feef272e 100644 --- a/enigma/src/test/java/cuchaz/enigma/TokenChecker.java +++ b/enigma/src/test/java/cuchaz/enigma/TokenChecker.java | |||
| @@ -12,11 +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 cuchaz.enigma.analysis.ClassCache; | ||
| 16 | import cuchaz.enigma.analysis.EntryReference; | 15 | import cuchaz.enigma.analysis.EntryReference; |
| 17 | import cuchaz.enigma.source.SourceIndex; | 16 | import cuchaz.enigma.classprovider.CachingClassProvider; |
| 17 | import cuchaz.enigma.classprovider.JarClassProvider; | ||
| 18 | import cuchaz.enigma.source.*; | 18 | import cuchaz.enigma.source.*; |
| 19 | import cuchaz.enigma.source.Token; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | 19 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 21 | 20 | ||
| 22 | import java.io.IOException; | 21 | import java.io.IOException; |
| @@ -28,8 +27,8 @@ public class TokenChecker { | |||
| 28 | private final Decompiler decompiler; | 27 | private final Decompiler decompiler; |
| 29 | 28 | ||
| 30 | protected TokenChecker(Path path) throws IOException { | 29 | protected TokenChecker(Path path) throws IOException { |
| 31 | ClassCache classCache = ClassCache.of(path); | 30 | CachingClassProvider classProvider = new CachingClassProvider(new JarClassProvider(path)); |
| 32 | decompiler = Decompilers.PROCYON.create(classCache, new SourceSettings(false, false)); | 31 | decompiler = Decompilers.PROCYON.create(classProvider, new SourceSettings(false, false)); |
| 33 | } | 32 | } |
| 34 | 33 | ||
| 35 | protected String getDeclarationToken(Entry<?> entry) { | 34 | protected String getDeclarationToken(Entry<?> entry) { |
diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java index 65941e54..60c70b73 100644 --- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java +++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java | |||
| @@ -14,6 +14,7 @@ package cuchaz.enigma.translation.mapping; | |||
| 14 | import cuchaz.enigma.Enigma; | 14 | import cuchaz.enigma.Enigma; |
| 15 | import cuchaz.enigma.EnigmaProject; | 15 | import cuchaz.enigma.EnigmaProject; |
| 16 | import cuchaz.enigma.ProgressListener; | 16 | import cuchaz.enigma.ProgressListener; |
| 17 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
| 17 | import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; | 18 | import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; |
| 18 | 19 | ||
| 19 | import java.nio.file.Path; | 20 | import java.nio.file.Path; |
| @@ -30,7 +31,7 @@ public final class TestTinyV2InnerClasses { | |||
| 30 | 31 | ||
| 31 | // @Test | 32 | // @Test |
| 32 | public void testMappings() throws Exception { | 33 | public void testMappings() throws Exception { |
| 33 | EnigmaProject project = Enigma.create().openJar(jar, ProgressListener.none()); | 34 | EnigmaProject project = Enigma.create().openJar(jar, new ClasspathClassProvider(), ProgressListener.none()); |
| 34 | project.setMappings(EnigmaMappingsReader.DIRECTORY.read(mappings, ProgressListener.none(), project.getEnigma().getProfile().getMappingSaveParameters())); | 35 | project.setMappings(EnigmaMappingsReader.DIRECTORY.read(mappings, ProgressListener.none(), project.getEnigma().getProfile().getMappingSaveParameters())); |
| 35 | 36 | ||
| 36 | } | 37 | } |