From d0b6f4b62a3271b8c6bcfa831d0d8f25851ee428 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 10 Sep 2025 12:53:26 +0100 Subject: Add a way for plugins to invalidate data (mappings, javadocs, decompile), and add a way for them to listen for this. --- .../src/main/java/cuchaz/enigma/EnigmaProject.java | 29 ++++++++++++++++++++ .../cuchaz/enigma/api/DataInvalidationEvent.java | 31 ++++++++++++++++++++++ .../enigma/api/DataInvalidationListener.java | 6 +++++ .../java/cuchaz/enigma/api/view/ProjectView.java | 19 +++++++++++++ .../enigma/classhandle/ClassHandleProvider.java | 20 ++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 enigma/src/main/java/cuchaz/enigma/api/DataInvalidationEvent.java create mode 100644 enigma/src/main/java/cuchaz/enigma/api/DataInvalidationListener.java (limited to 'enigma/src') diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 96ad433..1e4afa2 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; import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -17,11 +18,14 @@ import java.util.jar.JarOutputStream; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.api.DataInvalidationEvent; +import cuchaz.enigma.api.DataInvalidationListener; import cuchaz.enigma.api.service.NameProposalService; import cuchaz.enigma.api.service.ObfuscationTestService; import cuchaz.enigma.api.view.ProjectView; @@ -57,6 +61,8 @@ public class EnigmaProject implements ProjectView { private EntryRemapper mapper; + private final List dataInvalidationListeners = new ArrayList<>(); + public EnigmaProject(Enigma enigma, List jarPaths, ClassProvider classProvider, JarIndex jarIndex, byte[] jarChecksum) { if (jarChecksum.length != 20) { throw new IllegalArgumentException(); @@ -334,6 +340,29 @@ public class EnigmaProject implements ProjectView { return (T) mapper.extendedDeobfuscate((Translatable) entry).getValue(); } + @Override + public void addDataInvalidationListener(DataInvalidationListener listener) { + dataInvalidationListeners.add(listener); + } + + @Override + public void invalidateData(@Nullable Collection classes, DataInvalidationEvent.InvalidationType type) { + DataInvalidationEvent event = new DataInvalidationEvent() { + @Override + @Nullable + public Collection getClasses() { + return classes; + } + + @Override + public InvalidationType getType() { + return type; + } + }; + + dataInvalidationListeners.forEach(listener -> listener.onDataInvalidated(event)); + } + public static final class SourceExport { public final Collection decompiled; 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 0000000..812ad39 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationEvent.java @@ -0,0 +1,31 @@ +package cuchaz.enigma.api; + +import java.util.Collection; + +import org.jetbrains.annotations.Nullable; + +public interface DataInvalidationEvent { + /** + * The classes for which the invalidation applies, or {@code null} if the invalidation applies to all classes. + */ + @Nullable + Collection getClasses(); + + InvalidationType getType(); + + enum InvalidationType { + /** + * Only mappings are being invalidated. + */ + MAPPINGS, + /** + * Javadocs are being invalidated. This also implies {@link #MAPPINGS}. + */ + JAVADOC, + /** + * Context passed to the decompiler, such as the bytecode input or other parameters, is being invalidated. This + * also implies {@link #JAVADOC} and {@link #MAPPINGS}. + */ + DECOMPILE, + } +} 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 0000000..a07b109 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/api/DataInvalidationListener.java @@ -0,0 +1,6 @@ +package cuchaz.enigma.api; + +@FunctionalInterface +public interface DataInvalidationListener { + void onDataInvalidated(DataInvalidationEvent event); +} 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 e07645a..eae776b 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 @@ package cuchaz.enigma.api.view; +import java.util.Collection; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import cuchaz.enigma.api.DataInvalidationEvent; +import cuchaz.enigma.api.DataInvalidationListener; import cuchaz.enigma.api.view.entry.EntryView; public interface ProjectView { T deobfuscate(T entry); + + void addDataInvalidationListener(DataInvalidationListener listener); + + default void invalidateData(DataInvalidationEvent.InvalidationType type) { + invalidateData((Collection) null, type); + } + + default void invalidateData(String className, DataInvalidationEvent.InvalidationType type) { + invalidateData(List.of(className), type); + } + + void invalidateData(@Nullable Collection classes, DataInvalidationEvent.InvalidationType type); } diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java index f4bbb1d..f441bf4 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 { }); } + public void invalidate() { + withLock(lock.readLock(), () -> { + handles.values().forEach(Entry::invalidate); + }); + } + + public void invalidate(ClassEntry entry) { + withLock(lock.readLock(), () -> { + Entry e = handles.get(entry); + + if (e != null) { + e.invalidate(); + } + + if (entry.isInnerClass()) { + this.invalidate(entry.getOuterClass()); + } + }); + } + private void deleteEntry(Entry entry) { withLock(lock.writeLock(), () -> { handles.remove(entry.entry); -- cgit v1.2.3