diff options
6 files changed, 147 insertions, 9 deletions
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 3609427d..a9795bce 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -46,6 +46,8 @@ import cuchaz.enigma.analysis.MethodInheritanceTreeNode; | |||
| 46 | import cuchaz.enigma.analysis.MethodReferenceTreeNode; | 46 | import cuchaz.enigma.analysis.MethodReferenceTreeNode; |
| 47 | import cuchaz.enigma.analysis.StructureTreeNode; | 47 | import cuchaz.enigma.analysis.StructureTreeNode; |
| 48 | import cuchaz.enigma.analysis.StructureTreeOptions; | 48 | import cuchaz.enigma.analysis.StructureTreeOptions; |
| 49 | import cuchaz.enigma.api.DataInvalidationEvent; | ||
| 50 | import cuchaz.enigma.api.DataInvalidationListener; | ||
| 49 | import cuchaz.enigma.api.service.ObfuscationTestService; | 51 | import cuchaz.enigma.api.service.ObfuscationTestService; |
| 50 | import cuchaz.enigma.api.view.GuiView; | 52 | import cuchaz.enigma.api.view.GuiView; |
| 51 | import cuchaz.enigma.api.view.entry.EntryReferenceView; | 53 | import cuchaz.enigma.api.view.entry.EntryReferenceView; |
| @@ -94,7 +96,7 @@ import cuchaz.enigma.utils.Utils; | |||
| 94 | import cuchaz.enigma.utils.validation.PrintValidatable; | 96 | import cuchaz.enigma.utils.validation.PrintValidatable; |
| 95 | import cuchaz.enigma.utils.validation.ValidationContext; | 97 | import cuchaz.enigma.utils.validation.ValidationContext; |
| 96 | 98 | ||
| 97 | public class GuiController implements ClientPacketHandler, GuiView { | 99 | public class GuiController implements ClientPacketHandler, GuiView, DataInvalidationListener { |
| 98 | private final Gui gui; | 100 | private final Gui gui; |
| 99 | public final Enigma enigma; | 101 | public final Enigma enigma; |
| 100 | 102 | ||
| @@ -132,6 +134,7 @@ public class GuiController implements ClientPacketHandler, GuiView { | |||
| 132 | 134 | ||
| 133 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { | 135 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { |
| 134 | project = enigma.openJars(jarPaths, new ClasspathClassProvider(), progress); | 136 | project = enigma.openJars(jarPaths, new ClasspathClassProvider(), progress); |
| 137 | project.addDataInvalidationListener(this); | ||
| 135 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); | 138 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); |
| 136 | chp = new ClassHandleProvider(project, UiConfig.getDecompiler().service); | 139 | chp = new ClassHandleProvider(project, UiConfig.getDecompiler().service); |
| 137 | SwingUtilities.invokeLater(() -> { | 140 | SwingUtilities.invokeLater(() -> { |
| @@ -177,7 +180,7 @@ public class GuiController implements ClientPacketHandler, GuiView { | |||
| 177 | loadedMappingPath = path; | 180 | loadedMappingPath = path; |
| 178 | 181 | ||
| 179 | refreshClasses(); | 182 | refreshClasses(); |
| 180 | chp.invalidateJavadoc(); | 183 | project.invalidateData(DataInvalidationEvent.InvalidationType.JAVADOC); |
| 181 | } catch (MappingParseException e) { | 184 | } catch (MappingParseException e) { |
| 182 | JOptionPane.showMessageDialog(gui.getFrame(), e.getMessage()); | 185 | JOptionPane.showMessageDialog(gui.getFrame(), e.getMessage()); |
| 183 | } | 186 | } |
| @@ -192,7 +195,7 @@ public class GuiController implements ClientPacketHandler, GuiView { | |||
| 192 | 195 | ||
| 193 | project.setMappings(mappings); | 196 | project.setMappings(mappings); |
| 194 | refreshClasses(); | 197 | refreshClasses(); |
| 195 | chp.invalidateJavadoc(); | 198 | project.invalidateData(DataInvalidationEvent.InvalidationType.JAVADOC); |
| 196 | } | 199 | } |
| 197 | 200 | ||
| 198 | public MappingFormat getLoadedMappingFormat() { | 201 | public MappingFormat getLoadedMappingFormat() { |
| @@ -252,7 +255,7 @@ public class GuiController implements ClientPacketHandler, GuiView { | |||
| 252 | 255 | ||
| 253 | this.gui.setMappingsFile(null); | 256 | this.gui.setMappingsFile(null); |
| 254 | refreshClasses(); | 257 | refreshClasses(); |
| 255 | chp.invalidateJavadoc(); | 258 | project.invalidateData(DataInvalidationEvent.InvalidationType.JAVADOC); |
| 256 | } | 259 | } |
| 257 | 260 | ||
| 258 | public void reloadAll() { | 261 | public void reloadAll() { |
| @@ -573,12 +576,11 @@ public class GuiController implements ClientPacketHandler, GuiView { | |||
| 573 | this.gui.moveClassTree(target, prev.targetName() == null, mapping.targetName() == null); | 576 | this.gui.moveClassTree(target, prev.targetName() == null, mapping.targetName() == null); |
| 574 | } | 577 | } |
| 575 | 578 | ||
| 576 | if (!Objects.equals(prev.targetName(), mapping.targetName())) { | ||
| 577 | this.chp.invalidateMapped(); | ||
| 578 | } | ||
| 579 | |||
| 580 | if (!Objects.equals(prev.javadoc(), mapping.javadoc())) { | 579 | if (!Objects.equals(prev.javadoc(), mapping.javadoc())) { |
| 581 | this.chp.invalidateJavadoc(target.getTopLevelClass()); | 580 | project.invalidateData(target.getTopLevelClass().getFullName(), DataInvalidationEvent.InvalidationType.JAVADOC); |
| 581 | // invalidateJavadoc implies invalidateMapped, so no need to check for that too | ||
| 582 | } else if (!Objects.equals(prev.targetName(), mapping.targetName())) { | ||
| 583 | project.invalidateData(DataInvalidationEvent.InvalidationType.MAPPINGS); | ||
| 582 | } | 584 | } |
| 583 | 585 | ||
| 584 | gui.showStructure(gui.getActiveEditor()); | 586 | gui.showStructure(gui.getActiveEditor()); |
| @@ -677,4 +679,35 @@ public class GuiController implements ClientPacketHandler, GuiView { | |||
| 677 | public void updateUserList(List<String> users) { | 679 | public void updateUserList(List<String> users) { |
| 678 | gui.setUserList(users); | 680 | gui.setUserList(users); |
| 679 | } | 681 | } |
| 682 | |||
| 683 | @Override | ||
| 684 | public void onDataInvalidated(DataInvalidationEvent event) { | ||
| 685 | Objects.requireNonNull(project, "Invalidating data when no project is open"); | ||
| 686 | |||
| 687 | if (event.getClasses() == null) { | ||
| 688 | switch (event.getType()) { | ||
| 689 | case MAPPINGS -> chp.invalidateMapped(); | ||
| 690 | case JAVADOC -> chp.invalidateJavadoc(); | ||
| 691 | case DECOMPILE -> chp.invalidate(); | ||
| 692 | } | ||
| 693 | } else { | ||
| 694 | switch (event.getType()) { | ||
| 695 | case MAPPINGS -> { | ||
| 696 | for (String clazz : event.getClasses()) { | ||
| 697 | chp.invalidateMapped(new ClassEntry(clazz)); | ||
| 698 | } | ||
| 699 | } | ||
| 700 | case JAVADOC -> { | ||
| 701 | for (String clazz : event.getClasses()) { | ||
| 702 | chp.invalidateJavadoc(new ClassEntry(clazz)); | ||
| 703 | } | ||
| 704 | } | ||
| 705 | case DECOMPILE -> { | ||
| 706 | for (String clazz : event.getClasses()) { | ||
| 707 | chp.invalidate(new ClassEntry(clazz)); | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 711 | } | ||
| 712 | } | ||
| 680 | } | 713 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 96ad433b..1e4afa2c 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java | |||
| @@ -6,6 +6,7 @@ import java.io.PrintWriter; | |||
| 6 | import java.io.StringWriter; | 6 | import java.io.StringWriter; |
| 7 | import java.nio.file.Files; | 7 | import java.nio.file.Files; |
| 8 | import java.nio.file.Path; | 8 | import java.nio.file.Path; |
| 9 | import java.util.ArrayList; | ||
| 9 | import java.util.Collection; | 10 | import java.util.Collection; |
| 10 | import java.util.List; | 11 | import java.util.List; |
| 11 | import java.util.Map; | 12 | import java.util.Map; |
| @@ -17,11 +18,14 @@ import java.util.jar.JarOutputStream; | |||
| 17 | import java.util.stream.Collectors; | 18 | import java.util.stream.Collectors; |
| 18 | import java.util.stream.Stream; | 19 | import java.util.stream.Stream; |
| 19 | 20 | ||
| 21 | import org.jetbrains.annotations.Nullable; | ||
| 20 | import org.objectweb.asm.ClassWriter; | 22 | import org.objectweb.asm.ClassWriter; |
| 21 | import org.objectweb.asm.tree.ClassNode; | 23 | import org.objectweb.asm.tree.ClassNode; |
| 22 | 24 | ||
| 23 | import cuchaz.enigma.analysis.EntryReference; | 25 | import cuchaz.enigma.analysis.EntryReference; |
| 24 | import cuchaz.enigma.analysis.index.JarIndex; | 26 | import cuchaz.enigma.analysis.index.JarIndex; |
| 27 | import cuchaz.enigma.api.DataInvalidationEvent; | ||
| 28 | import cuchaz.enigma.api.DataInvalidationListener; | ||
| 25 | import cuchaz.enigma.api.service.NameProposalService; | 29 | import cuchaz.enigma.api.service.NameProposalService; |
| 26 | import cuchaz.enigma.api.service.ObfuscationTestService; | 30 | import cuchaz.enigma.api.service.ObfuscationTestService; |
| 27 | import cuchaz.enigma.api.view.ProjectView; | 31 | import cuchaz.enigma.api.view.ProjectView; |
| @@ -57,6 +61,8 @@ public class EnigmaProject implements ProjectView { | |||
| 57 | 61 | ||
| 58 | private EntryRemapper mapper; | 62 | private EntryRemapper mapper; |
| 59 | 63 | ||
| 64 | private final List<DataInvalidationListener> dataInvalidationListeners = new ArrayList<>(); | ||
| 65 | |||
| 60 | public EnigmaProject(Enigma enigma, List<Path> jarPaths, ClassProvider classProvider, JarIndex jarIndex, byte[] jarChecksum) { | 66 | public EnigmaProject(Enigma enigma, List<Path> jarPaths, ClassProvider classProvider, JarIndex jarIndex, byte[] jarChecksum) { |
| 61 | if (jarChecksum.length != 20) { | 67 | if (jarChecksum.length != 20) { |
| 62 | throw new IllegalArgumentException(); | 68 | throw new IllegalArgumentException(); |
| @@ -334,6 +340,29 @@ public class EnigmaProject implements ProjectView { | |||
| 334 | return (T) mapper.extendedDeobfuscate((Translatable) entry).getValue(); | 340 | return (T) mapper.extendedDeobfuscate((Translatable) entry).getValue(); |
| 335 | } | 341 | } |
| 336 | 342 | ||
| 343 | @Override | ||
| 344 | public void addDataInvalidationListener(DataInvalidationListener listener) { | ||
| 345 | dataInvalidationListeners.add(listener); | ||
| 346 | } | ||
| 347 | |||
| 348 | @Override | ||
| 349 | public void invalidateData(@Nullable Collection<String> classes, DataInvalidationEvent.InvalidationType type) { | ||
| 350 | DataInvalidationEvent event = new DataInvalidationEvent() { | ||
| 351 | @Override | ||
| 352 | @Nullable | ||
| 353 | public Collection<String> getClasses() { | ||
| 354 | return classes; | ||
| 355 | } | ||
| 356 | |||
| 357 | @Override | ||
| 358 | public InvalidationType getType() { | ||
| 359 | return type; | ||
| 360 | } | ||
| 361 | }; | ||
| 362 | |||
| 363 | dataInvalidationListeners.forEach(listener -> listener.onDataInvalidated(event)); | ||
| 364 | } | ||
| 365 | |||
| 337 | public static final class SourceExport { | 366 | public static final class SourceExport { |
| 338 | public final Collection<ClassSource> decompiled; | 367 | public final Collection<ClassSource> decompiled; |
| 339 | 368 | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationEvent.java b/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationEvent.java new file mode 100644 index 00000000..812ad397 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationEvent.java | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | package cuchaz.enigma.api; | ||
| 2 | |||
| 3 | import java.util.Collection; | ||
| 4 | |||
| 5 | import org.jetbrains.annotations.Nullable; | ||
| 6 | |||
| 7 | public interface DataInvalidationEvent { | ||
| 8 | /** | ||
| 9 | * The classes for which the invalidation applies, or {@code null} if the invalidation applies to all classes. | ||
| 10 | */ | ||
| 11 | @Nullable | ||
| 12 | Collection<String> getClasses(); | ||
| 13 | |||
| 14 | InvalidationType getType(); | ||
| 15 | |||
| 16 | enum InvalidationType { | ||
| 17 | /** | ||
| 18 | * Only mappings are being invalidated. | ||
| 19 | */ | ||
| 20 | MAPPINGS, | ||
| 21 | /** | ||
| 22 | * Javadocs are being invalidated. This also implies {@link #MAPPINGS}. | ||
| 23 | */ | ||
| 24 | JAVADOC, | ||
| 25 | /** | ||
| 26 | * Context passed to the decompiler, such as the bytecode input or other parameters, is being invalidated. This | ||
| 27 | * also implies {@link #JAVADOC} and {@link #MAPPINGS}. | ||
| 28 | */ | ||
| 29 | DECOMPILE, | ||
| 30 | } | ||
| 31 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationListener.java b/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationListener.java new file mode 100644 index 00000000..a07b109d --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationListener.java | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | package cuchaz.enigma.api; | ||
| 2 | |||
| 3 | @FunctionalInterface | ||
| 4 | public interface DataInvalidationListener { | ||
| 5 | void onDataInvalidated(DataInvalidationEvent event); | ||
| 6 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/api/view/ProjectView.java b/enigma/src/main/java/cuchaz/enigma/api/view/ProjectView.java index e07645a7..eae776bc 100644 --- a/enigma/src/main/java/cuchaz/enigma/api/view/ProjectView.java +++ b/enigma/src/main/java/cuchaz/enigma/api/view/ProjectView.java | |||
| @@ -1,7 +1,26 @@ | |||
| 1 | package cuchaz.enigma.api.view; | 1 | package cuchaz.enigma.api.view; |
| 2 | 2 | ||
| 3 | import java.util.Collection; | ||
| 4 | import java.util.List; | ||
| 5 | |||
| 6 | import org.jetbrains.annotations.Nullable; | ||
| 7 | |||
| 8 | import cuchaz.enigma.api.DataInvalidationEvent; | ||
| 9 | import cuchaz.enigma.api.DataInvalidationListener; | ||
| 3 | import cuchaz.enigma.api.view.entry.EntryView; | 10 | import cuchaz.enigma.api.view.entry.EntryView; |
| 4 | 11 | ||
| 5 | public interface ProjectView { | 12 | public interface ProjectView { |
| 6 | <T extends EntryView> T deobfuscate(T entry); | 13 | <T extends EntryView> T deobfuscate(T entry); |
| 14 | |||
| 15 | void addDataInvalidationListener(DataInvalidationListener listener); | ||
| 16 | |||
| 17 | default void invalidateData(DataInvalidationEvent.InvalidationType type) { | ||
| 18 | invalidateData((Collection<String>) null, type); | ||
| 19 | } | ||
| 20 | |||
| 21 | default void invalidateData(String className, DataInvalidationEvent.InvalidationType type) { | ||
| 22 | invalidateData(List.of(className), type); | ||
| 23 | } | ||
| 24 | |||
| 25 | void invalidateData(@Nullable Collection<String> classes, DataInvalidationEvent.InvalidationType type); | ||
| 7 | } | 26 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java index f4bbb1da..f441bf49 100644 --- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java | |||
| @@ -163,6 +163,26 @@ public final class ClassHandleProvider { | |||
| 163 | }); | 163 | }); |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | public void invalidate() { | ||
| 167 | withLock(lock.readLock(), () -> { | ||
| 168 | handles.values().forEach(Entry::invalidate); | ||
| 169 | }); | ||
| 170 | } | ||
| 171 | |||
| 172 | public void invalidate(ClassEntry entry) { | ||
| 173 | withLock(lock.readLock(), () -> { | ||
| 174 | Entry e = handles.get(entry); | ||
| 175 | |||
| 176 | if (e != null) { | ||
| 177 | e.invalidate(); | ||
| 178 | } | ||
| 179 | |||
| 180 | if (entry.isInnerClass()) { | ||
| 181 | this.invalidate(entry.getOuterClass()); | ||
| 182 | } | ||
| 183 | }); | ||
| 184 | } | ||
| 185 | |||
| 166 | private void deleteEntry(Entry entry) { | 186 | private void deleteEntry(Entry entry) { |
| 167 | withLock(lock.writeLock(), () -> { | 187 | withLock(lock.writeLock(), () -> { |
| 168 | handles.remove(entry.entry); | 188 | handles.remove(entry.entry); |