summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joe2025-09-10 12:53:26 +0100
committerGravatar modmuss2025-09-13 09:14:23 +0100
commitd0b6f4b62a3271b8c6bcfa831d0d8f25851ee428 (patch)
treef5fe271ed72cfa359502234bb21913d250c54571
parentAdd GuiService for plugins to make additions to the GUI. Also add enough cont... (diff)
downloadenigma-d0b6f4b62a3271b8c6bcfa831d0d8f25851ee428.tar.gz
enigma-d0b6f4b62a3271b8c6bcfa831d0d8f25851ee428.tar.xz
enigma-d0b6f4b62a3271b8c6bcfa831d0d8f25851ee428.zip
Add a way for plugins to invalidate data (mappings, javadocs, decompile), and add a way for them to listen for this.
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java51
-rw-r--r--enigma/src/main/java/cuchaz/enigma/EnigmaProject.java29
-rw-r--r--enigma/src/main/java/cuchaz/enigma/api/DataInvalidationEvent.java31
-rw-r--r--enigma/src/main/java/cuchaz/enigma/api/DataInvalidationListener.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/api/view/ProjectView.java19
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java20
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;
46import cuchaz.enigma.analysis.MethodReferenceTreeNode; 46import cuchaz.enigma.analysis.MethodReferenceTreeNode;
47import cuchaz.enigma.analysis.StructureTreeNode; 47import cuchaz.enigma.analysis.StructureTreeNode;
48import cuchaz.enigma.analysis.StructureTreeOptions; 48import cuchaz.enigma.analysis.StructureTreeOptions;
49import cuchaz.enigma.api.DataInvalidationEvent;
50import cuchaz.enigma.api.DataInvalidationListener;
49import cuchaz.enigma.api.service.ObfuscationTestService; 51import cuchaz.enigma.api.service.ObfuscationTestService;
50import cuchaz.enigma.api.view.GuiView; 52import cuchaz.enigma.api.view.GuiView;
51import cuchaz.enigma.api.view.entry.EntryReferenceView; 53import cuchaz.enigma.api.view.entry.EntryReferenceView;
@@ -94,7 +96,7 @@ import cuchaz.enigma.utils.Utils;
94import cuchaz.enigma.utils.validation.PrintValidatable; 96import cuchaz.enigma.utils.validation.PrintValidatable;
95import cuchaz.enigma.utils.validation.ValidationContext; 97import cuchaz.enigma.utils.validation.ValidationContext;
96 98
97public class GuiController implements ClientPacketHandler, GuiView { 99public 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;
6import java.io.StringWriter; 6import java.io.StringWriter;
7import java.nio.file.Files; 7import java.nio.file.Files;
8import java.nio.file.Path; 8import java.nio.file.Path;
9import java.util.ArrayList;
9import java.util.Collection; 10import java.util.Collection;
10import java.util.List; 11import java.util.List;
11import java.util.Map; 12import java.util.Map;
@@ -17,11 +18,14 @@ import java.util.jar.JarOutputStream;
17import java.util.stream.Collectors; 18import java.util.stream.Collectors;
18import java.util.stream.Stream; 19import java.util.stream.Stream;
19 20
21import org.jetbrains.annotations.Nullable;
20import org.objectweb.asm.ClassWriter; 22import org.objectweb.asm.ClassWriter;
21import org.objectweb.asm.tree.ClassNode; 23import org.objectweb.asm.tree.ClassNode;
22 24
23import cuchaz.enigma.analysis.EntryReference; 25import cuchaz.enigma.analysis.EntryReference;
24import cuchaz.enigma.analysis.index.JarIndex; 26import cuchaz.enigma.analysis.index.JarIndex;
27import cuchaz.enigma.api.DataInvalidationEvent;
28import cuchaz.enigma.api.DataInvalidationListener;
25import cuchaz.enigma.api.service.NameProposalService; 29import cuchaz.enigma.api.service.NameProposalService;
26import cuchaz.enigma.api.service.ObfuscationTestService; 30import cuchaz.enigma.api.service.ObfuscationTestService;
27import cuchaz.enigma.api.view.ProjectView; 31import 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 @@
1package cuchaz.enigma.api;
2
3import java.util.Collection;
4
5import org.jetbrains.annotations.Nullable;
6
7public 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 @@
1package cuchaz.enigma.api;
2
3@FunctionalInterface
4public 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 @@
1package cuchaz.enigma.api.view; 1package cuchaz.enigma.api.view;
2 2
3import java.util.Collection;
4import java.util.List;
5
6import org.jetbrains.annotations.Nullable;
7
8import cuchaz.enigma.api.DataInvalidationEvent;
9import cuchaz.enigma.api.DataInvalidationListener;
3import cuchaz.enigma.api.view.entry.EntryView; 10import cuchaz.enigma.api.view.entry.EntryView;
4 11
5public interface ProjectView { 12public 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);