diff --git a/enigma-server/src/main/java/org/quiltmc/enigma/network/DedicatedEnigmaServer.java b/enigma-server/src/main/java/org/quiltmc/enigma/network/DedicatedEnigmaServer.java index 0f3c14549..a2e28e23e 100644 --- a/enigma-server/src/main/java/org/quiltmc/enigma/network/DedicatedEnigmaServer.java +++ b/enigma-server/src/main/java/org/quiltmc/enigma/network/DedicatedEnigmaServer.java @@ -124,10 +124,10 @@ public static void main(String[] args) { EntryRemapper mappings; if (!Files.exists(mappingsFile)) { - mappings = EntryRemapper.mapped(enigma, project.getJarIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), new HashEntryTree<>(), enigma.getNameProposalServices()); + mappings = EntryRemapper.mapped(enigma, project.getCombinedIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), new HashEntryTree<>(), enigma.getNameProposalServices()); } else { Logger.info("Reading mappings..."); - mappings = EntryRemapper.mapped(enigma, project.getJarIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), readWriteService.get().read(mappingsFile), enigma.getNameProposalServices()); + mappings = EntryRemapper.mapped(enigma, project.getCombinedIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), readWriteService.get().read(mappingsFile), enigma.getNameProposalServices()); } PrintWriter log = new PrintWriter(Files.newBufferedWriter(logFile)); 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 bdd5c10cf..ef41cb534 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 @@ -682,7 +682,7 @@ public void createClient(String username, String ip, int port, char[] password) } public void createServer(String username, int port, char[] password) throws IOException { - this.server = new IntegratedEnigmaServer(this.project.getJarChecksum(), password, EntryRemapper.mapped(this.project.getEnigma(), this.project.getJarIndex(), this.project.getMappingsIndex(), new HashEntryTree<>(this.project.getRemapper().getJarProposedMappings()), new HashEntryTree<>(this.project.getRemapper().getDeobfMappings()), this.project.getEnigma().getNameProposalServices()), port); + this.server = new IntegratedEnigmaServer(this.project.getJarChecksum(), password, EntryRemapper.mapped(this.project.getEnigma(), this.project.getCombinedIndex(), this.project.getMappingsIndex(), new HashEntryTree<>(this.project.getRemapper().getJarProposedMappings()), new HashEntryTree<>(this.project.getRemapper().getDeobfMappings()), this.project.getEnigma().getNameProposalServices()), port); this.server.start(); this.client = new IntegratedEnigmaClient(this, "127.0.0.1", port); this.client.connect(); diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java b/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java index 39e7b5b7e..3347d00b3 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java @@ -1,12 +1,22 @@ package org.quiltmc.enigma.api; +import com.google.common.base.Predicates; +import com.google.common.collect.Streams; import com.google.common.io.MoreFiles; -import org.quiltmc.enigma.api.analysis.index.jar.JarIndex; +import org.objectweb.asm.tree.ClassNode; +import org.quiltmc.enigma.api.analysis.index.jar.CombinedJarIndex; +import org.quiltmc.enigma.api.analysis.index.jar.EntryIndex; +import org.quiltmc.enigma.api.analysis.index.jar.InheritanceIndex; import org.quiltmc.enigma.api.analysis.index.jar.LibrariesJarIndex; import org.quiltmc.enigma.api.analysis.index.jar.MainJarIndex; +import org.quiltmc.enigma.api.analysis.index.jar.ReferenceIndex; import org.quiltmc.enigma.api.analysis.index.mapping.MappingsIndex; +import org.quiltmc.enigma.api.class_provider.ClasspathClassProvider; import org.quiltmc.enigma.api.class_provider.ProjectClassProvider; import org.quiltmc.enigma.api.translation.mapping.serde.MappingParseException; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; import org.quiltmc.enigma.impl.analysis.ClassLoaderClassProvider; import org.quiltmc.enigma.api.service.EnigmaService; import org.quiltmc.enigma.api.service.EnigmaServiceContext; @@ -25,6 +35,8 @@ import org.quiltmc.enigma.api.translation.mapping.tree.EntryTree; import org.quiltmc.enigma.api.translation.mapping.tree.HashEntryTree; import org.quiltmc.enigma.api.translation.representation.entry.Entry; +import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex; +import org.quiltmc.enigma.impl.analysis.index.IndexClassVisitor; import org.quiltmc.enigma.util.Either; import org.quiltmc.enigma.util.I18n; import org.quiltmc.enigma.util.Utils; @@ -43,15 +55,20 @@ import java.nio.file.attribute.BasicFileAttributes; import java.sql.DriverManager; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class Enigma { public static final String NAME = "Enigma"; @@ -80,19 +97,27 @@ public static Builder builder() { public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, ProgressListener progress) throws IOException { JarClassProvider jarClassProvider = new JarClassProvider(path); - JarIndex index = MainJarIndex.empty(); - JarIndex libIndex = LibrariesJarIndex.empty(); + AbstractJarIndex jarIndex = MainJarIndex.empty(); + AbstractJarIndex libIndex = LibrariesJarIndex.empty(); + AbstractJarIndex comboIndex = CombinedJarIndex.empty(); ClassLoaderClassProvider jreProvider = new ClassLoaderClassProvider(DriverManager.class.getClassLoader()); - CombiningClassProvider librariesProvider = new CombiningClassProvider(jreProvider, libraryClassProvider); - ClassProvider mainProjectProvider = new ObfuscationFixClassProvider(new CachingClassProvider(jarClassProvider), index); + ClasspathClassProvider javaClassProvider = new ClasspathClassProvider(); + CombiningClassProvider librariesProvider = new CombiningClassProvider(jreProvider, javaClassProvider, libraryClassProvider); + ClassProvider mainProjectProvider = new ObfuscationFixClassProvider(new CachingClassProvider(jarClassProvider), jarIndex); ProjectClassProvider projectClassProvider = new ProjectClassProvider(mainProjectProvider, librariesProvider); // main index - this.index(index, projectClassProvider, progress); + this.index(jarIndex, projectClassProvider, progress, "jar", false, null); + + // TODO make filtering toggleable with arg once JavaClassProvider is used + final Predicate mainReferencedPredicate = this.createMainReferencedPredicate(jarIndex, projectClassProvider); // lib index - this.index(libIndex, projectClassProvider, progress); + this.index(libIndex, projectClassProvider, progress, "jar", true, mainReferencedPredicate); + + // combined main and lib index + this.index(comboIndex, projectClassProvider, progress, "combined", true, mainReferencedPredicate); // name proposal var nameProposalServices = this.getNameProposalServices(); @@ -103,7 +128,7 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog int j = 1; for (var service : nameProposalServices) { progress.step(j++, I18n.translateFormatted("progress.jar.name_proposal.proposer", service.getId())); - Map, EntryMapping> proposed = service.getProposedNames(this, index); + Map, EntryMapping> proposed = service.getProposedNames(this, jarIndex); if (proposed != null) { for (var entry : proposed.entrySet()) { @@ -118,23 +143,89 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog MappingsIndex mappingsIndex = MappingsIndex.empty(); mappingsIndex.indexMappings(proposedNames, progress); - return new EnigmaProject(this, path, mainProjectProvider, index, libIndex, mappingsIndex, proposedNames, Utils.zipSha1(path)); + return new EnigmaProject(this, path, mainProjectProvider, jarIndex, libIndex, comboIndex, mappingsIndex, proposedNames, Utils.zipSha1(path)); } - private void index(JarIndex index, ProjectClassProvider classProvider, ProgressListener progress) { - boolean libraries = index instanceof LibrariesJarIndex; - String progressKey = libraries ? "libs" : "jar"; - index.indexJar(classProvider, progress); + private Predicate createMainReferencedPredicate(AbstractJarIndex mainIndex, ProjectClassProvider classProvider) { + final EntryIndex mainEntryIndex = mainIndex.getIndex(EntryIndex.class); + + final EntryIndex entryIndex = new EntryIndex(); + final ReferenceIndex referenceIndex = new ReferenceIndex(); + final InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); + + final Collection allClassNames = classProvider.getClassNames(); + for (final String className : allClassNames) { + final ClassNode classNode = Objects.requireNonNull(classProvider.get(className)); + classNode.accept(new IndexClassVisitor(entryIndex, Enigma.ASM_VERSION)); + classNode.accept(new IndexClassVisitor(referenceIndex, Enigma.ASM_VERSION)); + classNode.accept(new IndexClassVisitor(inheritanceIndex, Enigma.ASM_VERSION)); + } + + return className -> { + final ClassEntry classEntry = new ClassEntry(className); + if (mainEntryIndex.hasClass(classEntry)) { + return true; + } + + if (inheritanceIndex.getChildren(classEntry).stream().anyMatch(mainEntryIndex::hasClass)) { + return true; + } + + final boolean typeReferenced = Streams + .concat( + referenceIndex.getReferencesToClass(classEntry).stream(), + referenceIndex.getMethodTypeReferencesToClass(classEntry).stream(), + referenceIndex.getFieldTypeReferencesToClass(classEntry).stream() + ) + .anyMatch(reference -> + mainEntryIndex.hasClass(reference.entry) || mainEntryIndex.hasEntry(reference.context) + ); + + if (typeReferenced) { + return true; + } + + final List mainMethods = mainIndex.getChildrenByClass().values().stream() + .flatMap(entry -> entry instanceof MethodEntry method ? Stream.of(method) : Stream.empty()) + .toList(); + + final boolean methodReferenced = mainMethods.stream() + .flatMap(method -> referenceIndex.getMethodsReferencedBy(method).stream()) + .map(MethodEntry::getParent) + .anyMatch(classEntry::equals); + if (methodReferenced) { + return true; + } + + // field referenced + return mainMethods.stream() + .flatMap(method -> referenceIndex.getFieldsReferencedBy(method).stream()) + .map(FieldEntry::getParent) + .anyMatch(classEntry::equals); + }; + } + + private void index( + AbstractJarIndex index, ProjectClassProvider classProvider, ProgressListener progress, String progressKey, + boolean includesLibraries, @Nullable Predicate classNameFilter + ) { + if (classNameFilter == null) { + index.indexJar(classProvider, progress); + classNameFilter = Predicates.alwaysTrue(); + } else { + index.indexJar(classProvider, progress, classNameFilter); + } List indexers = this.services.get(JarIndexerService.TYPE); progress.init(indexers.size(), I18n.translate("progress." + progressKey + ".custom_indexing")); int i = 1; for (var service : indexers) { - if (!(libraries && !service.shouldIndexLibraries())) { + if (!(includesLibraries && !service.shouldIndexLibraries())) { progress.step(i++, I18n.translateFormatted("progress." + progressKey + ".custom_indexing.indexer", service.getId())); - var names = libraries ? classProvider.getLibraryClassNames() : classProvider.getMainClassNames(); - Set scope = new HashSet<>(names); + Set scope = index.getIndexableClassNames(classProvider).stream() + .filter(classNameFilter) + .collect(Collectors.toCollection(HashSet::new)); service.acceptJar(scope, classProvider, index); } } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/EnigmaProject.java b/enigma/src/main/java/org/quiltmc/enigma/api/EnigmaProject.java index 8cc423f3a..4dedcf79b 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/EnigmaProject.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/EnigmaProject.java @@ -9,6 +9,8 @@ import org.quiltmc.enigma.api.analysis.index.mapping.MappingsIndex; import org.quiltmc.enigma.api.service.ObfuscationTestService; import org.quiltmc.enigma.api.source.TokenType; +import org.quiltmc.enigma.api.translation.mapping.EntryResolver; +import org.quiltmc.enigma.api.translation.mapping.ResolutionStrategy; import org.quiltmc.enigma.api.translation.mapping.tree.EntryTreeUtil; import org.quiltmc.enigma.api.translation.mapping.tree.HashEntryTree; import org.quiltmc.enigma.impl.bytecode.translator.TranslationClassVisitor; @@ -41,9 +43,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; @@ -56,22 +60,25 @@ public class EnigmaProject { private final ClassProvider classProvider; private final JarIndex jarIndex; private final JarIndex libIndex; + private final JarIndex combinedIndex; private final byte[] jarChecksum; + private final Map libraryMethodOverrideCache = new HashMap<>(); private EntryRemapper remapper; private MappingsIndex mappingsIndex; - public EnigmaProject(Enigma enigma, Path jarPath, ClassProvider classProvider, JarIndex jarIndex, JarIndex libIndex, MappingsIndex mappingsIndex, EntryTree proposedNames, byte[] jarChecksum) { + public EnigmaProject(Enigma enigma, Path jarPath, ClassProvider classProvider, JarIndex jarIndex, JarIndex libIndex, JarIndex combinedIndex, MappingsIndex mappingsIndex, EntryTree proposedNames, byte[] jarChecksum) { Preconditions.checkArgument(jarChecksum.length == 20); this.enigma = enigma; this.jarPath = jarPath; this.classProvider = classProvider; this.jarIndex = jarIndex; this.libIndex = libIndex; + this.combinedIndex = combinedIndex; this.jarChecksum = jarChecksum; this.mappingsIndex = mappingsIndex; - this.remapper = EntryRemapper.mapped(enigma, jarIndex, this.mappingsIndex, proposedNames, new HashEntryTree<>(), this.enigma.getNameProposalServices()); + this.remapper = EntryRemapper.mapped(this.enigma, this.combinedIndex, this.mappingsIndex, proposedNames, new HashEntryTree<>(), this.enigma.getNameProposalServices()); } /** @@ -90,12 +97,12 @@ public void setMappings(@Nullable EntryTree mappings, ProgressList EntryTree mergedTree = EntryTreeUtil.merge(jarProposedMappings, mappings); this.mappingsIndex.indexMappings(mergedTree, progress); - this.remapper = EntryRemapper.mapped(this.enigma, this.jarIndex, this.mappingsIndex, jarProposedMappings, mappings, this.enigma.getNameProposalServices()); + this.remapper = EntryRemapper.mapped(this.enigma, this.combinedIndex, this.mappingsIndex, jarProposedMappings, mappings, this.enigma.getNameProposalServices()); } else if (!jarProposedMappings.isEmpty()) { this.mappingsIndex.indexMappings(jarProposedMappings, progress); - this.remapper = EntryRemapper.mapped(this.enigma, this.jarIndex, this.mappingsIndex, jarProposedMappings, new HashEntryTree<>(), this.enigma.getNameProposalServices()); + this.remapper = EntryRemapper.mapped(this.enigma, this.combinedIndex, this.mappingsIndex, jarProposedMappings, new HashEntryTree<>(), this.enigma.getNameProposalServices()); } else { - this.remapper = EntryRemapper.empty(this.enigma, this.jarIndex, this.enigma.getNameProposalServices()); + this.remapper = EntryRemapper.empty(this.enigma, this.combinedIndex, this.enigma.getNameProposalServices()); } // update dynamically proposed names @@ -118,6 +125,14 @@ public JarIndex getJarIndex() { return this.jarIndex; } + public JarIndex getLibIndex() { + return this.libIndex; + } + + public JarIndex getCombinedIndex() { + return this.combinedIndex; + } + public MappingsIndex getMappingsIndex() { return this.mappingsIndex; } @@ -199,6 +214,10 @@ public boolean isRenamable(Entry obfEntry) { || isEnumValueOfMethod(parent, obfMethodEntry))) { return false; } + + if (this.isLibraryMethodOverride(obfMethodEntry)) { + return false; + } } else if (obfEntry instanceof LocalVariableEntry localEntry && !localEntry.isArgument()) { return false; } else if (obfEntry instanceof LocalVariableEntry localEntry && localEntry.isArgument()) { @@ -216,6 +235,36 @@ public boolean isRenamable(Entry obfEntry) { return this.jarIndex.getIndex(EntryIndex.class).hasEntry(obfEntry); } + private boolean isLibraryMethodOverride(MethodEntry methodEntry) { + final Boolean cached = this.libraryMethodOverrideCache.get(methodEntry); + if (cached != null) { + return cached; + } else { + if (this.combinedIndex.getIndex(EntryIndex.class).hasMethod(methodEntry)) { + final EntryResolver combinedResolver = this.combinedIndex.getEntryResolver(); + final Set equivalents = combinedResolver.resolveEquivalentMethods(methodEntry); + final Set roots = equivalents.stream() + .flatMap(equivalent -> combinedResolver.resolveEntry(equivalent, ResolutionStrategy.RESOLVE_ROOT).stream()) + .collect(Collectors.toSet()); + + final Set equivalentsAndRoots = Stream + .concat(equivalents.stream(), roots.stream()) + .collect(Collectors.toSet()); + + final EntryIndex jarEntryIndex = this.jarIndex.getIndex(EntryIndex.class); + final boolean anyNonJar = equivalentsAndRoots.stream().anyMatch(method -> !jarEntryIndex.hasMethod(method)); + + equivalentsAndRoots.forEach(method -> this.libraryMethodOverrideCache.put(method, anyNonJar)); + + return anyNonJar; + } else { + this.libraryMethodOverrideCache.put(methodEntry, false); + + return false; + } + } + } + private static boolean isEnumValueOfMethod(ClassDefEntry parent, MethodEntry method) { return parent != null && parent.isEnum() && method.getName().equals("valueOf") && method.getDesc().toString().equals("(Ljava/lang/String;)L" + parent.getFullName() + ";"); } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedJarIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedJarIndex.java new file mode 100644 index 000000000..c4953f4d4 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedJarIndex.java @@ -0,0 +1,42 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import org.quiltmc.enigma.api.class_provider.ProjectClassProvider; +import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex; + +import java.util.Collection; + +public class CombinedJarIndex extends AbstractJarIndex { + public CombinedJarIndex( + EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers + ) { + super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); + } + + /** + * Creates an empty index, configured to use all built-in indexers. + * @return the newly created index + */ + public static CombinedJarIndex empty() { + EntryIndex entryIndex = new EntryIndex(); + ReferenceIndex referenceIndex = new ReferenceIndex(); + InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); + LambdaIndex lambdaIndex = new LambdaIndex(); + return new CombinedJarIndex( + entryIndex, inheritanceIndex, referenceIndex, + new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex), + // required by MappingValidator + lambdaIndex + ); + } + + @Override + public String getTranslationKey() { + return "progress.jar.indexing.combined"; + } + + @Override + public Collection getIndexableClassNames(ProjectClassProvider classProvider) { + return classProvider.getClassNames(); + } +} diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/InheritanceIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/InheritanceIndex.java index 88ca401e8..11aa6043e 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/InheritanceIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/InheritanceIndex.java @@ -24,7 +24,7 @@ public InheritanceIndex(EntryIndex entryIndex) { @Override public void indexClass(ClassDefEntry classEntry) { ClassEntry superClass = classEntry.getSuperClass(); - if (superClass != null && !superClass.getName().equals("java/lang/Object")) { + if (superClass != null) { this.indexParent(classEntry, superClass); } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/LibrariesJarIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/LibrariesJarIndex.java index 2ecbf735e..34ec1150a 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/LibrariesJarIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/LibrariesJarIndex.java @@ -1,23 +1,30 @@ package org.quiltmc.enigma.api.analysis.index.jar; -import org.quiltmc.enigma.api.ProgressListener; import org.quiltmc.enigma.api.class_provider.ProjectClassProvider; import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex; +import java.util.Collection; + public class LibrariesJarIndex extends AbstractJarIndex { - public LibrariesJarIndex(JarIndexer... indexers) { - super(indexers); + public LibrariesJarIndex( + EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers + ) { + super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); } /** * Creates an empty index, configured to use all built-in indexers. * @return the newly created index */ - public static JarIndex empty() { + public static LibrariesJarIndex empty() { EntryIndex entryIndex = new EntryIndex(); ReferenceIndex referenceIndex = new ReferenceIndex(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); - return new LibrariesJarIndex(entryIndex, inheritanceIndex, referenceIndex, new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex)); + return new LibrariesJarIndex( + entryIndex, inheritanceIndex, referenceIndex, + new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex) + ); } @Override @@ -26,7 +33,7 @@ public String getTranslationKey() { } @Override - public void indexJar(ProjectClassProvider classProvider, ProgressListener progress) { - this.indexJar(classProvider.getLibraryClassNames(), classProvider, progress); + public Collection getIndexableClassNames(ProjectClassProvider classProvider) { + return classProvider.getLibraryClassNames(); } } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/MainJarIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/MainJarIndex.java index d3c0719a1..c6af0c08a 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/MainJarIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/MainJarIndex.java @@ -1,19 +1,23 @@ package org.quiltmc.enigma.api.analysis.index.jar; -import org.quiltmc.enigma.api.ProgressListener; import org.quiltmc.enigma.api.class_provider.ProjectClassProvider; import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex; +import java.util.Collection; + public class MainJarIndex extends AbstractJarIndex { - public MainJarIndex(JarIndexer... indexers) { - super(indexers); + public MainJarIndex( + EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers + ) { + super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); } /** * Creates an empty index, configured to use all built-in indexers. * @return the newly created index */ - public static JarIndex empty() { + public static MainJarIndex empty() { EntryIndex entryIndex = new EntryIndex(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); ReferenceIndex referenceIndex = new ReferenceIndex(); @@ -21,7 +25,10 @@ public static JarIndex empty() { PackageVisibilityIndex packageVisibilityIndex = new PackageVisibilityIndex(); EnclosingMethodIndex enclosingMethodIndex = new EnclosingMethodIndex(); LambdaIndex lambdaIndex = new LambdaIndex(); - return new MainJarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex, enclosingMethodIndex, lambdaIndex); + return new MainJarIndex( + entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, + packageVisibilityIndex, enclosingMethodIndex, lambdaIndex + ); } @Override @@ -30,7 +37,7 @@ public String getTranslationKey() { } @Override - public void indexJar(ProjectClassProvider classProvider, ProgressListener progress) { - this.indexJar(classProvider.getMainClassNames(), classProvider, progress); + public Collection getIndexableClassNames(ProjectClassProvider classProvider) { + return classProvider.getMainClassNames(); } } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndex.java index 7acfb2ad2..c81e488fb 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndex.java @@ -20,6 +20,7 @@ public class ReferenceIndex implements JarIndexer { private Multimap methodReferences = HashMultimap.create(); + private Multimap fieldReferences = HashMultimap.create(); private Multimap> referencesToMethods = HashMultimap.create(); private Multimap> referencesToClasses = HashMultimap.create(); @@ -82,6 +83,7 @@ public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referen @Override public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry, ReferenceTargetType targetType) { this.referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); + this.fieldReferences.put(callerEntry, referencedEntry); } @Override @@ -100,6 +102,7 @@ public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTarg @Override public void processIndex(JarIndex index) { this.methodReferences = this.remapReferences(index, this.methodReferences); + this.fieldReferences = this.remapReferences(index, this.fieldReferences); this.referencesToMethods = this.remapReferencesTo(index, this.referencesToMethods); this.referencesToClasses = this.remapReferencesTo(index, this.referencesToClasses); this.referencesToFields = this.remapReferencesTo(index, this.referencesToFields); @@ -139,6 +142,10 @@ public Collection getMethodsReferencedBy(MethodEntry entry) { return this.methodReferences.get(entry); } + public Collection getFieldsReferencedBy(MethodEntry entry) { + return this.fieldReferences.get(entry); + } + public Collection> getReferencesToField(FieldEntry entry) { return this.referencesToFields.get(entry); } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/class_provider/JavaClassProvider.java b/enigma/src/main/java/org/quiltmc/enigma/api/class_provider/JavaClassProvider.java new file mode 100644 index 000000000..b01b6db60 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/class_provider/JavaClassProvider.java @@ -0,0 +1,73 @@ +package org.quiltmc.enigma.api.class_provider; + +import com.google.common.collect.ImmutableSet; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.analysis.AnalyzerException; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.InputStream; +import java.lang.module.ModuleReader; +import java.lang.module.ResolvedModule; +import java.util.Collection; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Provides java.* classes. + * + *

Currently unusable because of {@link AnalyzerException}s. + */ +public class JavaClassProvider implements ClassProvider { + private static final String CLASS_EXTENSION = ".class"; + private static final Pattern JAVA_CLASS_PATTERN = Pattern.compile("^java/.*" + Pattern.quote(CLASS_EXTENSION) + "$"); + + private static final ImmutableSet UN_ANALYZABLE_CLASS_NAMES = ImmutableSet.of( + "java/lang/module/ModuleDescriptor", + "java/io/PrintStream" + ); + + @Nullable + private Set classes; + + @Nullable + @Override + public ClassNode get(String name) { + try (InputStream in = Object.class.getResourceAsStream("/" + name + CLASS_EXTENSION)) { + if (in == null) { + return null; + } + + ClassNode node = new ClassNode(); + new ClassReader(in).accept(node, 0); + return node; + } catch (IOException e) { + return null; + } + } + + @Override + public Collection getClassNames() { + if (this.classes == null) { + this.classes = Object.class.getModule().getLayer().configuration().modules().stream() + .map(ResolvedModule::reference) + .flatMap(ref -> { + try (ModuleReader reader = ref.open()) { + return reader.list(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .filter(resource -> JAVA_CLASS_PATTERN.matcher(resource).find()) + .map(javaClass -> javaClass.substring(0, javaClass.length() - CLASS_EXTENSION.length())) + // HACK: these cause AnalyzerException's + .filter(className -> !UN_ANALYZABLE_CLASS_NAMES.contains(className)) + .filter(className -> this.get(className) != null) + .collect(Collectors.toSet()); + } + + return this.classes; + } +} diff --git a/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/ClassLoaderClassProvider.java b/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/ClassLoaderClassProvider.java index e4468c8ed..7e2dbb441 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/ClassLoaderClassProvider.java +++ b/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/ClassLoaderClassProvider.java @@ -43,6 +43,6 @@ public ClassNode get(String name) { @Override public Collection getClassNames() { - return List.of("java.lang.Object", "java.lang.Record"); + return List.of("java/lang/Object", "java/lang/Record"); } } diff --git a/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/AbstractJarIndex.java b/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/AbstractJarIndex.java index a248b5295..893ec9efd 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/AbstractJarIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/AbstractJarIndex.java @@ -2,6 +2,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimap; import org.quiltmc.enigma.api.Enigma; @@ -12,7 +13,9 @@ import org.quiltmc.enigma.api.analysis.index.jar.InheritanceIndex; import org.quiltmc.enigma.api.analysis.index.jar.JarIndex; import org.quiltmc.enigma.api.analysis.index.jar.JarIndexer; +import org.quiltmc.enigma.api.analysis.index.jar.ReferenceIndex; import org.quiltmc.enigma.api.class_provider.ClassProvider; +import org.quiltmc.enigma.api.class_provider.ProjectClassProvider; import org.quiltmc.enigma.api.translation.mapping.EntryResolver; import org.quiltmc.enigma.api.translation.mapping.IndexEntryResolver; import org.quiltmc.enigma.api.translation.representation.Lambda; @@ -25,16 +28,19 @@ import org.quiltmc.enigma.api.translation.representation.entry.ParentedEntry; import org.quiltmc.enigma.util.I18n; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; public abstract class AbstractJarIndex implements JarIndex { private final Set indexedClasses = new HashSet<>(); - private final Map, JarIndexer> indexers = new LinkedHashMap<>(); + private final ImmutableMap, JarIndexer> indexers; private final IndexEntryResolver entryResolver; private final Multimap methodImplementations = HashMultimap.create(); @@ -45,12 +51,23 @@ public abstract class AbstractJarIndex implements JarIndex { /** * Creates a new empty index with all provided indexers. * Indexers will be run in the order they're passed to this constructor. - * @param indexers the indexers to use + * + * @param entryIndex the entry index + * @param inheritanceIndex the inheritance index + * @param referenceIndex the reference index + * @param bridgeMethodIndex the bridge method index + * @param otherIndexers other indexers to use */ - public AbstractJarIndex(JarIndexer... indexers) { - for (JarIndexer indexer : indexers) { - this.indexers.put(indexer.getClass(), indexer); - } + public AbstractJarIndex( + EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers + ) { + this.indexers = Stream + .concat( + Stream.of(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex), + Arrays.stream(otherIndexers) + ) + .collect(ImmutableMap.toImmutableMap(JarIndexer::getClass, Function.identity())); this.entryResolver = new IndexEntryResolver(this); this.childrenByClass = ArrayListMultimap.create(); @@ -71,8 +88,37 @@ public T getIndex(Class clazz) { } } + @Override + public void indexJar(ProjectClassProvider classProvider, ProgressListener progress) { + this.indexJar(this.getIndexableClassNames(classProvider), classProvider, progress); + } + + /** + * Runs every configured indexer over the provided jar on classes matching the passed {@code classNameFilter}. + * + * @param classProvider a class provider containing all classes in the jar and libraries + * @param progress a progress listener to track index completion + * @param classNameFilter a predicate used to filter out class names + */ + public void indexJar( + ProjectClassProvider classProvider, ProgressListener progress, Predicate classNameFilter + ) { + final Collection classNames = this.getIndexableClassNames(classProvider).stream() + .filter(classNameFilter) + .collect(Collectors.toSet()); + this.indexJar(classNames, classProvider, progress); + } + + /** + * Gets the names of classes this indexer should index. + * + * @param classProvider a class provider containing all classes in the jar + */ + public abstract Collection getIndexableClassNames(ProjectClassProvider classProvider); + /** * Runs every configured indexer over the provided jar. + * * @param classProvider a class provider containing all classes in the jar * @param progress a progress listener to track index completion */ diff --git a/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/IndexClassVisitor.java b/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/IndexClassVisitor.java index 3afc26e9b..eea0e0fcf 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/IndexClassVisitor.java +++ b/enigma/src/main/java/org/quiltmc/enigma/impl/analysis/index/IndexClassVisitor.java @@ -1,6 +1,5 @@ package org.quiltmc.enigma.impl.analysis.index; -import org.quiltmc.enigma.api.analysis.index.jar.JarIndex; import org.quiltmc.enigma.impl.analysis.MethodNodeWithAction; import org.quiltmc.enigma.api.analysis.index.jar.JarIndexer; import org.quiltmc.enigma.api.translation.representation.ParameterAccessFlags; @@ -15,7 +14,7 @@ public class IndexClassVisitor extends ClassVisitor { private final JarIndexer indexer; private ClassDefEntry classEntry; - public IndexClassVisitor(JarIndex indexer, int api) { + public IndexClassVisitor(JarIndexer indexer, int api) { super(api); this.indexer = indexer; } diff --git a/enigma/src/main/resources/lang/en_us.json b/enigma/src/main/resources/lang/en_us.json index 001898785..d4ceee494 100644 --- a/enigma/src/main/resources/lang/en_us.json +++ b/enigma/src/main/resources/lang/en_us.json @@ -194,10 +194,15 @@ "progress.libs.custom_indexing": "Running custom indexers", "progress.libs.custom_indexing.indexer": "Running %s", "progress.libs.custom_indexing.finished": "Done!", + "progress.combined.custom_indexing": "Running custom indexers", + "progress.combined.custom_indexing.indexer": "Running %s", + "progress.combined.custom_indexing.finished": "Done!", "progress.jar.name_proposal": "Proposing names...", "progress.jar.name_proposal.proposer": "Running %s", "progress.jar.name_proposal.finished": "Done!", "progress.jar.indexing.jar": "Indexing JAR...", + "progress.jar.indexing.libraries": "Indexing Libraries...", + "progress.jar.indexing.combined": "Indexing Combined JAR and Libraries...", "progress.jar.indexing.process.jar": "JAR...", "progress.jar.indexing.process.bridge_methods": "Bridge methods...", "progress.jar.indexing.process.references": "Entry references...", diff --git a/enigma/src/test/java/org/quiltmc/enigma/TestDeobfed.java b/enigma/src/test/java/org/quiltmc/enigma/TestDeobfed.java index 8e0908905..edc168a9d 100644 --- a/enigma/src/test/java/org/quiltmc/enigma/TestDeobfed.java +++ b/enigma/src/test/java/org/quiltmc/enigma/TestDeobfed.java @@ -58,7 +58,8 @@ public void obfEntries() { TestEntryFactory.newClass("h$b$a$b"), TestEntryFactory.newClass("i"), TestEntryFactory.newClass("i$a"), - TestEntryFactory.newClass("i$b") + TestEntryFactory.newClass("i$b"), + TestEntryFactory.newClass("j") )); } diff --git a/enigma/src/test/java/org/quiltmc/enigma/TestIsRenamable.java b/enigma/src/test/java/org/quiltmc/enigma/TestIsRenamable.java index 636fde91c..02380db54 100644 --- a/enigma/src/test/java/org/quiltmc/enigma/TestIsRenamable.java +++ b/enigma/src/test/java/org/quiltmc/enigma/TestIsRenamable.java @@ -7,6 +7,8 @@ import org.quiltmc.enigma.api.EnigmaProject; import org.quiltmc.enigma.api.ProgressListener; import org.quiltmc.enigma.api.class_provider.ClasspathClassProvider; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; import java.nio.file.Files; import java.nio.file.Path; @@ -42,4 +44,15 @@ public void obfEntries() { var wait = TestEntryFactory.newMethod("b", "wait", "()V"); Assertions.assertFalse(obfProject.isRenamable(wait)); } + + @Test + public void libraryOverrides() { + final ClassEntry toStringOverrider = TestEntryFactory.newClass("j"); + + final MethodEntry toString = TestEntryFactory.newMethod( + toStringOverrider, "toString", "()Ljava/lang/String;" + ); + + Assertions.assertFalse(obfProject.isRenamable(toString)); + } } diff --git a/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexInheritanceTree.java b/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexInheritanceTree.java index 6222259b6..d03a570f8 100644 --- a/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexInheritanceTree.java +++ b/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexInheritanceTree.java @@ -30,6 +30,7 @@ public class TestJarIndexInheritanceTree { public static final Path JAR = TestUtil.obfJar("inheritance_tree"); + private static final ClassEntry OBJECT_CLASS = TestEntryFactory.newClass("java/lang/Object"); private static final ClassEntry BASE_CLASS = TestEntryFactory.newClass("a"); private static final ClassEntry SUB_CLASS_A = TestEntryFactory.newClass("b"); private static final ClassEntry SUB_CLASS_AA = TestEntryFactory.newClass("d"); @@ -57,24 +58,23 @@ public void translationIndex() { InheritanceIndex index = this.index.getIndex(InheritanceIndex.class); // base class - assertThat(index.getParents(BASE_CLASS), is(empty())); - assertThat(index.getAncestors(BASE_CLASS), is(empty())); - assertThat(index.getChildren(BASE_CLASS), containsInAnyOrder(SUB_CLASS_A, SUB_CLASS_B - )); + assertThat(index.getParents(BASE_CLASS), containsInAnyOrder(OBJECT_CLASS)); + assertThat(index.getAncestors(BASE_CLASS), containsInAnyOrder(OBJECT_CLASS)); + assertThat(index.getChildren(BASE_CLASS), containsInAnyOrder(SUB_CLASS_A, SUB_CLASS_B)); // subclass a assertThat(index.getParents(SUB_CLASS_A), contains(BASE_CLASS)); - assertThat(index.getAncestors(SUB_CLASS_A), containsInAnyOrder(BASE_CLASS)); + assertThat(index.getAncestors(SUB_CLASS_A), containsInAnyOrder(BASE_CLASS, OBJECT_CLASS)); assertThat(index.getChildren(SUB_CLASS_A), contains(SUB_CLASS_AA)); // subclass aa assertThat(index.getParents(SUB_CLASS_AA), contains(SUB_CLASS_A)); - assertThat(index.getAncestors(SUB_CLASS_AA), containsInAnyOrder(SUB_CLASS_A, BASE_CLASS)); + assertThat(index.getAncestors(SUB_CLASS_AA), containsInAnyOrder(SUB_CLASS_A, BASE_CLASS, OBJECT_CLASS)); assertThat(index.getChildren(SUB_CLASS_AA), is(empty())); // subclass b assertThat(index.getParents(SUB_CLASS_B), contains(BASE_CLASS)); - assertThat(index.getAncestors(SUB_CLASS_B), containsInAnyOrder(BASE_CLASS)); + assertThat(index.getAncestors(SUB_CLASS_B), containsInAnyOrder(BASE_CLASS, OBJECT_CLASS)); assertThat(index.getChildren(SUB_CLASS_B), is(empty())); } diff --git a/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexLoneClass.java b/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexLoneClass.java index cb71d6750..57b535f4f 100644 --- a/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexLoneClass.java +++ b/enigma/src/test/java/org/quiltmc/enigma/TestJarIndexLoneClass.java @@ -24,8 +24,10 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; +import javax.swing.tree.TreeNode; import java.nio.file.Path; import java.util.Collection; +import java.util.Iterator; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; @@ -33,6 +35,9 @@ public class TestJarIndexLoneClass { public static final Path JAR = TestUtil.obfJar("lone_class"); + + private static final ClassEntry OBJECT_CLASS = TestEntryFactory.newClass("java/lang/Object"); + private final JarIndex index; public TestJarIndexLoneClass() throws Exception { @@ -52,10 +57,10 @@ public void obfEntries() { @Test public void translationIndex() { InheritanceIndex inheritanceIndex = this.index.getIndex(InheritanceIndex.class); - assertThat(inheritanceIndex.getParents(new ClassEntry("a")), is(empty())); - assertThat(inheritanceIndex.getParents(new ClassEntry("org/quiltmc/enigma/input/Keep")), is(empty())); - assertThat(inheritanceIndex.getAncestors(new ClassEntry("a")), is(empty())); - assertThat(inheritanceIndex.getAncestors(new ClassEntry("org/quiltmc/enigma/input/Keep")), is(empty())); + assertThat(inheritanceIndex.getParents(new ClassEntry("a")), containsInAnyOrder(OBJECT_CLASS)); + assertThat(inheritanceIndex.getParents(new ClassEntry("org/quiltmc/enigma/input/Keep")), containsInAnyOrder(OBJECT_CLASS)); + assertThat(inheritanceIndex.getAncestors(new ClassEntry("a")), containsInAnyOrder(OBJECT_CLASS)); + assertThat(inheritanceIndex.getAncestors(new ClassEntry("org/quiltmc/enigma/input/Keep")), containsInAnyOrder(OBJECT_CLASS)); assertThat(inheritanceIndex.getChildren(new ClassEntry("a")), is(empty())); assertThat(inheritanceIndex.getChildren(new ClassEntry("org/quiltmc/enigma/input/Keep")), is(empty())); } @@ -72,10 +77,27 @@ public void access() { @Test public void classInheritance() { IndexTreeBuilder treeBuilder = new IndexTreeBuilder(this.index); - ClassInheritanceTreeNode node = treeBuilder.buildClassInheritance(VoidTranslator.INSTANCE, TestEntryFactory.newClass("a")); - assertThat(node, is(not(nullValue()))); - assertThat(node.getClassName(), is("a")); - assertThat(node.getChildCount(), is(0)); + ClassInheritanceTreeNode root = treeBuilder.buildClassInheritance(VoidTranslator.INSTANCE, TestEntryFactory.newClass("a")); + assertThat(root, is(not(nullValue()))); + + final ClassInheritanceTreeNode a = findChild(root, "a"); + assertThat(a, is(not(nullValue()))); + assertThat(a.getChildCount(), is(0)); + } + + private static ClassInheritanceTreeNode findChild(ClassInheritanceTreeNode root, String childName) { + final Iterator childItr = root.children().asIterator(); + while (childItr.hasNext()) { + final TreeNode childNode = childItr.next(); + if ( + childNode instanceof ClassInheritanceTreeNode inheritanceNode + && inheritanceNode.getClassName().equals(childName) + ) { + return inheritanceNode; + } + } + + return null; } @Test @@ -130,7 +152,7 @@ public void behaviorReferences() { @Test public void interfaces() { - assertThat(this.index.getIndex(InheritanceIndex.class).getParents(new ClassEntry("a")), is(empty())); + assertThat(this.index.getIndex(InheritanceIndex.class).getParents(new ClassEntry("a")), containsInAnyOrder(OBJECT_CLASS)); } @Test diff --git a/enigma/src/test/java/org/quiltmc/enigma/input/translation/J_ToStringOverrider.java b/enigma/src/test/java/org/quiltmc/enigma/input/translation/J_ToStringOverrider.java new file mode 100644 index 000000000..d270ab9a0 --- /dev/null +++ b/enigma/src/test/java/org/quiltmc/enigma/input/translation/J_ToStringOverrider.java @@ -0,0 +1,8 @@ +package org.quiltmc.enigma.input.translation; + +public class J_ToStringOverrider { + @Override + public String toString() { + return "must not rename"; + } +} diff --git a/enigma/src/test/java/org/quiltmc/enigma/translation/mapping/TestMappingValidator.java b/enigma/src/test/java/org/quiltmc/enigma/translation/mapping/TestMappingValidator.java index 4c210a3f1..872aa8086 100644 --- a/enigma/src/test/java/org/quiltmc/enigma/translation/mapping/TestMappingValidator.java +++ b/enigma/src/test/java/org/quiltmc/enigma/translation/mapping/TestMappingValidator.java @@ -175,6 +175,14 @@ public void conflictingMethods() { assertMessages(vc, Message.NON_UNIQUE_NAME_CLASS); } + @RepeatedTest(value = 2, name = REPEATED_TEST_NAME) + public void conflictingWithObjectMethods() { + ValidationContext vc = TestUtil.newVC(); + remapper.validatePutMapping(vc, TestEntryFactory.newMethod("a", "b", "()V"), new EntryMapping("toString")); + + assertMessages(vc, Message.NON_UNIQUE_NAME_CLASS); + } + @RepeatedTest(value = 2, name = REPEATED_TEST_NAME) public void testParameterNames() { MethodEntry method = TestEntryFactory.newMethod("a", "a", "(II)I"); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ac23c5adf..b2d8a70d0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ shadow = "8.1.1" guava = "33.2.1-jre" gson = "2.10.1" -asm = "9.7.1" +asm = "9.8" jopt = "6.0-alpha-3" flatlaf = "3.4" syntaxpain = "0.1.5"