diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/gui/GuiController.java')
| -rw-r--r-- | src/main/java/cuchaz/enigma/gui/GuiController.java | 87 |
1 files changed, 54 insertions, 33 deletions
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index dc5010c..25a1057 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -13,8 +13,9 @@ package cuchaz.enigma.gui; | |||
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import com.google.common.util.concurrent.ThreadFactoryBuilder; | 15 | import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| 16 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 16 | import cuchaz.enigma.Enigma; |
| 17 | import cuchaz.enigma.*; | 17 | import cuchaz.enigma.EnigmaProfile; |
| 18 | import cuchaz.enigma.EnigmaProject; | ||
| 18 | import cuchaz.enigma.analysis.*; | 19 | import cuchaz.enigma.analysis.*; |
| 19 | import cuchaz.enigma.api.service.ObfuscationTestService; | 20 | import cuchaz.enigma.api.service.ObfuscationTestService; |
| 20 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; | 21 | import cuchaz.enigma.bytecode.translators.SourceFixVisitor; |
| @@ -23,6 +24,7 @@ import cuchaz.enigma.gui.dialog.ProgressDialog; | |||
| 23 | import cuchaz.enigma.gui.stats.StatsGenerator; | 24 | import cuchaz.enigma.gui.stats.StatsGenerator; |
| 24 | import cuchaz.enigma.gui.stats.StatsMember; | 25 | import cuchaz.enigma.gui.stats.StatsMember; |
| 25 | import cuchaz.enigma.gui.util.History; | 26 | import cuchaz.enigma.gui.util.History; |
| 27 | import cuchaz.enigma.source.*; | ||
| 26 | import cuchaz.enigma.throwables.MappingParseException; | 28 | import cuchaz.enigma.throwables.MappingParseException; |
| 27 | import cuchaz.enigma.translation.Translator; | 29 | import cuchaz.enigma.translation.Translator; |
| 28 | import cuchaz.enigma.translation.mapping.*; | 30 | import cuchaz.enigma.translation.mapping.*; |
| @@ -36,16 +38,16 @@ import cuchaz.enigma.utils.I18n; | |||
| 36 | import cuchaz.enigma.utils.ReadableToken; | 38 | import cuchaz.enigma.utils.ReadableToken; |
| 37 | import cuchaz.enigma.utils.Utils; | 39 | import cuchaz.enigma.utils.Utils; |
| 38 | import org.objectweb.asm.Opcodes; | 40 | import org.objectweb.asm.Opcodes; |
| 41 | import org.objectweb.asm.tree.ClassNode; | ||
| 39 | 42 | ||
| 40 | import javax.annotation.Nullable; | 43 | import javax.annotation.Nullable; |
| 41 | import javax.swing.*; | 44 | import javax.swing.JOptionPane; |
| 42 | import java.awt.*; | 45 | import java.awt.Desktop; |
| 43 | import java.awt.event.ItemEvent; | 46 | import java.awt.event.ItemEvent; |
| 44 | import java.io.*; | 47 | import java.io.*; |
| 45 | import java.nio.file.Path; | 48 | import java.nio.file.Path; |
| 46 | import java.util.Collection; | 49 | import java.util.Collection; |
| 47 | import java.util.List; | 50 | import java.util.List; |
| 48 | import java.util.Optional; | ||
| 49 | import java.util.Set; | 51 | import java.util.Set; |
| 50 | import java.util.concurrent.CompletableFuture; | 52 | import java.util.concurrent.CompletableFuture; |
| 51 | import java.util.concurrent.ExecutorService; | 53 | import java.util.concurrent.ExecutorService; |
| @@ -65,19 +67,23 @@ public class GuiController { | |||
| 65 | public final Enigma enigma; | 67 | public final Enigma enigma; |
| 66 | 68 | ||
| 67 | public EnigmaProject project; | 69 | public EnigmaProject project; |
| 68 | private SourceProvider sourceProvider; | 70 | private DecompilerService decompilerService; |
| 71 | private Decompiler decompiler; | ||
| 69 | private IndexTreeBuilder indexTreeBuilder; | 72 | private IndexTreeBuilder indexTreeBuilder; |
| 70 | 73 | ||
| 71 | private Path loadedMappingPath; | 74 | private Path loadedMappingPath; |
| 72 | private MappingFormat loadedMappingFormat; | 75 | private MappingFormat loadedMappingFormat; |
| 73 | 76 | ||
| 74 | private DecompiledClassSource currentSource; | 77 | private DecompiledClassSource currentSource; |
| 78 | private Source uncommentedSource; | ||
| 75 | 79 | ||
| 76 | public GuiController(Gui gui, EnigmaProfile profile) { | 80 | public GuiController(Gui gui, EnigmaProfile profile) { |
| 77 | this.gui = gui; | 81 | this.gui = gui; |
| 78 | this.enigma = Enigma.builder() | 82 | this.enigma = Enigma.builder() |
| 79 | .setProfile(profile) | 83 | .setProfile(profile) |
| 80 | .build(); | 84 | .build(); |
| 85 | |||
| 86 | decompilerService = Config.getInstance().decompiler.service; | ||
| 81 | } | 87 | } |
| 82 | 88 | ||
| 83 | public boolean isDirty() { | 89 | public boolean isDirty() { |
| @@ -89,19 +95,27 @@ public class GuiController { | |||
| 89 | 95 | ||
| 90 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { | 96 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { |
| 91 | project = enigma.openJar(jarPath, progress); | 97 | project = enigma.openJar(jarPath, progress); |
| 92 | |||
| 93 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); | 98 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); |
| 94 | 99 | decompiler = createDecompiler(); | |
| 95 | CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(project.getClassCache()); | ||
| 96 | typeLoader.addVisitor(visitor -> new SourceFixVisitor(Opcodes.ASM5, visitor, project.getJarIndex())); | ||
| 97 | sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader); | ||
| 98 | |||
| 99 | gui.onFinishOpenJar(jarPath.getFileName().toString()); | 100 | gui.onFinishOpenJar(jarPath.getFileName().toString()); |
| 100 | |||
| 101 | refreshClasses(); | 101 | refreshClasses(); |
| 102 | }); | 102 | }); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | private Decompiler createDecompiler() { | ||
| 106 | return decompilerService.create(name -> { | ||
| 107 | ClassNode node = project.getClassCache().getClassNode(name); | ||
| 108 | |||
| 109 | if (node == null) { | ||
| 110 | return null; | ||
| 111 | } | ||
| 112 | |||
| 113 | ClassNode fixedNode = new ClassNode(); | ||
| 114 | node.accept(new SourceFixVisitor(Opcodes.ASM7, fixedNode, project.getJarIndex())); | ||
| 115 | return fixedNode; | ||
| 116 | }, new SourceSettings(true, true)); | ||
| 117 | } | ||
| 118 | |||
| 105 | public void closeJar() { | 119 | public void closeJar() { |
| 106 | this.project = null; | 120 | this.project = null; |
| 107 | this.gui.onCloseJar(); | 121 | this.gui.onCloseJar(); |
| @@ -176,7 +190,7 @@ public class GuiController { | |||
| 176 | 190 | ||
| 177 | return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { | 191 | return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { |
| 178 | EnigmaProject.JarExport jar = project.exportRemappedJar(progress); | 192 | EnigmaProject.JarExport jar = project.exportRemappedJar(progress); |
| 179 | EnigmaProject.SourceExport source = jar.decompile(progress); | 193 | EnigmaProject.SourceExport source = jar.decompile(progress, decompilerService); |
| 180 | 194 | ||
| 181 | source.write(path, progress); | 195 | source.write(path, progress); |
| 182 | }); | 196 | }); |
| @@ -210,6 +224,7 @@ public class GuiController { | |||
| 210 | if (this.currentSource == null) { | 224 | if (this.currentSource == null) { |
| 211 | return null; | 225 | return null; |
| 212 | } | 226 | } |
| 227 | |||
| 213 | SourceIndex index = this.currentSource.getIndex(); | 228 | SourceIndex index = this.currentSource.getIndex(); |
| 214 | return new ReadableToken( | 229 | return new ReadableToken( |
| 215 | index.getLineNumber(token.start), | 230 | index.getLineNumber(token.start), |
| @@ -369,27 +384,27 @@ public class GuiController { | |||
| 369 | } | 384 | } |
| 370 | 385 | ||
| 371 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference) { | 386 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference) { |
| 372 | refreshCurrentClass(reference, false); | 387 | refreshCurrentClass(reference, RefreshMode.MINIMAL); |
| 373 | } | 388 | } |
| 374 | 389 | ||
| 375 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference, boolean forceDecomp) { | 390 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference, RefreshMode mode) { |
| 376 | if (currentSource != null) { | 391 | if (currentSource != null) { |
| 377 | loadClass(currentSource.getEntry(), () -> { | 392 | loadClass(currentSource.getEntry(), () -> { |
| 378 | if (reference != null) { | 393 | if (reference != null) { |
| 379 | showReference(reference); | 394 | showReference(reference); |
| 380 | } | 395 | } |
| 381 | }, forceDecomp); | 396 | }, mode); |
| 382 | } | 397 | } |
| 383 | } | 398 | } |
| 384 | 399 | ||
| 385 | private void loadClass(ClassEntry classEntry, Runnable callback) { | 400 | private void loadClass(ClassEntry classEntry, Runnable callback) { |
| 386 | loadClass(classEntry, callback, false); | 401 | loadClass(classEntry, callback, RefreshMode.MINIMAL); |
| 387 | } | 402 | } |
| 388 | 403 | ||
| 389 | private void loadClass(ClassEntry classEntry, Runnable callback, boolean forceDecomp) { | 404 | private void loadClass(ClassEntry classEntry, Runnable callback, RefreshMode mode) { |
| 390 | ClassEntry targetClass = classEntry.getOutermostClass(); | 405 | ClassEntry targetClass = classEntry.getOutermostClass(); |
| 391 | 406 | ||
| 392 | boolean requiresDecompile = forceDecomp || currentSource == null || !currentSource.getEntry().equals(targetClass); | 407 | boolean requiresDecompile = mode == RefreshMode.FULL || currentSource == null || !currentSource.getEntry().equals(targetClass); |
| 393 | if (requiresDecompile) { | 408 | if (requiresDecompile) { |
| 394 | currentSource = null; // Or the GUI may try to find a nonexistent token | 409 | currentSource = null; // Or the GUI may try to find a nonexistent token |
| 395 | gui.setEditorText(I18n.translate("info_panel.editor.class.decompiling")); | 410 | gui.setEditorText(I18n.translate("info_panel.editor.class.decompiling")); |
| @@ -397,8 +412,8 @@ public class GuiController { | |||
| 397 | 412 | ||
| 398 | DECOMPILER_SERVICE.submit(() -> { | 413 | DECOMPILER_SERVICE.submit(() -> { |
| 399 | try { | 414 | try { |
| 400 | if (requiresDecompile) { | 415 | if (requiresDecompile || mode == RefreshMode.JAVADOCS) { |
| 401 | currentSource = decompileSource(targetClass); | 416 | currentSource = decompileSource(targetClass, mode == RefreshMode.JAVADOCS); |
| 402 | } | 417 | } |
| 403 | 418 | ||
| 404 | remapSource(project.getMapper().getDeobfuscator()); | 419 | remapSource(project.getMapper().getDeobfuscator()); |
| @@ -410,21 +425,20 @@ public class GuiController { | |||
| 410 | }); | 425 | }); |
| 411 | } | 426 | } |
| 412 | 427 | ||
| 413 | private DecompiledClassSource decompileSource(ClassEntry targetClass) { | 428 | private DecompiledClassSource decompileSource(ClassEntry targetClass, boolean onlyRefreshJavadocs) { |
| 414 | try { | 429 | try { |
| 415 | CompilationUnit sourceTree = (CompilationUnit) sourceProvider.getSources(targetClass.getFullName()).clone(); | 430 | if (!onlyRefreshJavadocs || currentSource == null || !currentSource.getEntry().equals(targetClass)) { |
| 416 | if (sourceTree == null) { | 431 | uncommentedSource = decompiler.getSource(targetClass.getFullName()); |
| 417 | gui.setEditorText(I18n.translate("info_panel.editor.class.not_found") + " " + targetClass); | ||
| 418 | return DecompiledClassSource.text(targetClass, "Unable to find class"); | ||
| 419 | } | 432 | } |
| 420 | 433 | ||
| 421 | DropImportAstTransform.INSTANCE.run(sourceTree); | 434 | Source source = uncommentedSource.addJavadocs(project.getMapper()); |
| 422 | DropVarModifiersAstTransform.INSTANCE.run(sourceTree); | ||
| 423 | new AddJavadocsAstTransform(project.getMapper()).run(sourceTree); | ||
| 424 | 435 | ||
| 425 | String sourceString = sourceProvider.writeSourceToString(sourceTree); | 436 | if (source == null) { |
| 437 | gui.setEditorText(I18n.translate("info_panel.editor.class.not_found") + " " + targetClass); | ||
| 438 | return DecompiledClassSource.text(targetClass, "Unable to find class"); | ||
| 439 | } | ||
| 426 | 440 | ||
| 427 | SourceIndex index = SourceIndex.buildIndex(sourceString, sourceTree, true); | 441 | SourceIndex index = source.index(); |
| 428 | index.resolveReferences(project.getMapper().getObfResolver()); | 442 | index.resolveReferences(project.getMapper().getObfResolver()); |
| 429 | 443 | ||
| 430 | return new DecompiledClassSource(targetClass, index); | 444 | return new DecompiledClassSource(targetClass, index); |
| @@ -535,7 +549,7 @@ public class GuiController { | |||
| 535 | public void changeDocs(EntryReference<Entry<?>, Entry<?>> reference, String updatedDocs) { | 549 | public void changeDocs(EntryReference<Entry<?>, Entry<?>> reference, String updatedDocs) { |
| 536 | changeDoc(reference.getNameableEntry(), updatedDocs); | 550 | changeDoc(reference.getNameableEntry(), updatedDocs); |
| 537 | 551 | ||
| 538 | refreshCurrentClass(reference, true); | 552 | refreshCurrentClass(reference, RefreshMode.JAVADOCS); |
| 539 | } | 553 | } |
| 540 | 554 | ||
| 541 | public void changeDoc(Entry<?> obfEntry, String newDoc) { | 555 | public void changeDoc(Entry<?> obfEntry, String newDoc) { |
| @@ -582,4 +596,11 @@ public class GuiController { | |||
| 582 | } | 596 | } |
| 583 | }); | 597 | }); |
| 584 | } | 598 | } |
| 599 | |||
| 600 | public void setDecompiler(DecompilerService service) { | ||
| 601 | uncommentedSource = null; | ||
| 602 | decompilerService = service; | ||
| 603 | decompiler = createDecompiler(); | ||
| 604 | refreshCurrentClass(null, RefreshMode.FULL); | ||
| 605 | } | ||
| 585 | } | 606 | } |