diff --git a/enigma-swing/src/main/java/org/quiltmc/enigma/gui/Gui.java b/enigma-swing/src/main/java/org/quiltmc/enigma/gui/Gui.java index bb80edd1c..58791c22b 100644 --- a/enigma-swing/src/main/java/org/quiltmc/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/org/quiltmc/enigma/gui/Gui.java @@ -3,6 +3,7 @@ import org.quiltmc.enigma.api.Enigma; import org.quiltmc.enigma.api.EnigmaProfile; import org.quiltmc.enigma.api.analysis.EntryReference; +import org.quiltmc.enigma.api.analysis.index.jar.InheritanceIndex; import org.quiltmc.enigma.api.source.TokenType; import org.quiltmc.enigma.api.translation.mapping.EntryMapping; import org.quiltmc.enigma.api.translation.mapping.EntryRemapper; @@ -586,18 +587,28 @@ public void moveClassTree(ClassEntry classEntry, boolean updateSwingState, boole allClassesSelector.restoreExpansionState(expansionState); deobfuscatedClassSelector.restoreExpansionState(deobfuscatedPanelExpansionState); obfuscatedClassSelector.restoreExpansionState(obfuscatedPanelExpansionState); - this.reloadStats(classEntry); + this.reloadStats(classEntry, false); } } /** * Reloads stats for the provided class in all selectors. * @param classEntry the class to reload + * @param propagate whether to also reload ancestors of the class */ - public void reloadStats(ClassEntry classEntry) { + public void reloadStats(ClassEntry classEntry, boolean propagate) { + List toUpdate = new ArrayList<>(); + toUpdate.add(classEntry); + if (propagate) { + Collection parents = this.controller.getProject().getJarIndex().getIndex(InheritanceIndex.class).getAncestors(classEntry); + toUpdate.addAll(parents); + } + for (Docker value : this.dockerManager.getDockers()) { if (value instanceof ClassesDocker docker) { - docker.getClassSelector().reloadStats(classEntry); + for (ClassEntry entry : toUpdate) { + docker.getClassSelector().reloadStats(entry); + } } } } diff --git a/enigma-swing/src/main/java/org/quiltmc/enigma/gui/GuiController.java b/enigma-swing/src/main/java/org/quiltmc/enigma/gui/GuiController.java index 121015830..3fd9d9ec0 100644 --- a/enigma-swing/src/main/java/org/quiltmc/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/org/quiltmc/enigma/gui/GuiController.java @@ -576,7 +576,9 @@ private void applyChange0(ValidationContext vc, EntryChange change, boolean u boolean isNewOb = mapping.targetName() == null; this.gui.moveClassTree(target.getContainingClass(), updateSwingState, isOldOb, isNewOb); } else if (updateSwingState) { - this.gui.reloadStats(change.getTarget().getTopLevelClass()); + // update stat icons for classes that could have had their mappings changed by this update + boolean propagate = target instanceof FieldEntry || target instanceof MethodEntry || target instanceof LocalVariableEntry; + this.gui.reloadStats(change.getTarget().getTopLevelClass(), propagate); } } } diff --git a/enigma/src/test/java/org/quiltmc/enigma/stats/TestStatGenerationInheritance.java b/enigma/src/test/java/org/quiltmc/enigma/stats/TestStatGenerationInheritance.java new file mode 100644 index 000000000..9240184fb --- /dev/null +++ b/enigma/src/test/java/org/quiltmc/enigma/stats/TestStatGenerationInheritance.java @@ -0,0 +1,56 @@ +package org.quiltmc.enigma.stats; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.quiltmc.enigma.TestEntryFactory; +import org.quiltmc.enigma.TestUtil; +import org.quiltmc.enigma.api.Enigma; +import org.quiltmc.enigma.api.EnigmaProject; +import org.quiltmc.enigma.api.ProgressListener; +import org.quiltmc.enigma.api.class_provider.JarClassProvider; +import org.quiltmc.enigma.api.stats.StatType; +import org.quiltmc.enigma.api.stats.StatsGenerator; +import org.quiltmc.enigma.api.stats.StatsResult; +import org.quiltmc.enigma.api.translation.mapping.EntryMapping; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Set; + +public class TestStatGenerationInheritance { + private static final Path JAR = TestUtil.obfJar("interfaces"); + private static EnigmaProject project; + + @BeforeEach + void openProject() { + try { + Enigma enigma = Enigma.create(); + project = enigma.openJar(JAR, new JarClassProvider(JAR), ProgressListener.createEmpty()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + void testInterfacePropagation() { + // https://github.com/QuiltMC/enigma/issues/230 - verifying backend bit + ClassEntry interfaceEntry = TestEntryFactory.newClass("b"); + ClassEntry inheritorEntry = TestEntryFactory.newClass("a"); + + StatsResult interfaceResult = new StatsGenerator(project).generate(Set.of(StatType.METHODS), interfaceEntry, false); + Assertions.assertEquals(2, interfaceResult.getMappable()); + + // the inheritor does not own the method; it won't count towards its stats + StatsResult inheritorResult = new StatsGenerator(project).generate(Set.of(StatType.METHODS), inheritorEntry, false); + Assertions.assertEquals(0, inheritorResult.getMappable()); + + MethodEntry inheritedMethod = TestEntryFactory.newMethod(inheritorEntry, "a", "(D)D"); + project.getRemapper().putMapping(TestUtil.newVC(), inheritedMethod, new EntryMapping("mapped")); + + StatsResult interfaceResult2 = new StatsGenerator(project).generate(Set.of(StatType.METHODS), interfaceEntry, false); + Assertions.assertEquals(1, interfaceResult2.getMapped()); + } +} diff --git a/enigma/src/test/java/org/quiltmc/enigma/TestStatsGeneration.java b/enigma/src/test/java/org/quiltmc/enigma/stats/TestStatsGeneration.java similarity index 98% rename from enigma/src/test/java/org/quiltmc/enigma/TestStatsGeneration.java rename to enigma/src/test/java/org/quiltmc/enigma/stats/TestStatsGeneration.java index c4b13c10e..a6f08abc1 100644 --- a/enigma/src/test/java/org/quiltmc/enigma/TestStatsGeneration.java +++ b/enigma/src/test/java/org/quiltmc/enigma/stats/TestStatsGeneration.java @@ -1,5 +1,6 @@ -package org.quiltmc.enigma; +package org.quiltmc.enigma.stats; +import org.quiltmc.enigma.TestUtil; import org.quiltmc.enigma.api.Enigma; import org.quiltmc.enigma.api.EnigmaProject; import org.quiltmc.enigma.api.ProgressListener;