From 4410c3b43485c44d40e65254155ebace11f4b719 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 13:42:07 -0800 Subject: [PATCH 01/14] remove Enigma::openJar's mainReferencedPredicate make CombinedJarIndex's EntryIndex a combination of MainJarIndex's and LibrariesJarIndex's --- .../java/org/quiltmc/enigma/api/Enigma.java | 129 +++--------- .../index/jar/CombinedEntryIndex.java | 148 +++++++++++++ .../analysis/index/jar/CombinedJarIndex.java | 4 +- .../api/analysis/index/jar/EntryIndex.java | 165 +++------------ .../analysis/index/jar/EntryIndexImpl.java | 196 ++++++++++++++++++ .../api/analysis/index/jar/JarIndexer.java | 12 ++ .../analysis/index/jar/LibrariesJarIndex.java | 7 +- .../api/analysis/index/jar/MainJarIndex.java | 7 +- .../impl/analysis/index/AbstractJarIndex.java | 5 +- .../org/quiltmc/enigma/util/CombinedSet.java | 165 +++++++++++++++ 10 files changed, 588 insertions(+), 250 deletions(-) create mode 100644 enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java create mode 100644 enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java create mode 100644 enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java 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 308629b75..4c372d655 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java @@ -1,19 +1,14 @@ package org.quiltmc.enigma.api; import com.google.common.base.Preconditions; -import com.google.common.base.Predicates; import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.Streams; import com.google.common.io.MoreFiles; import org.jspecify.annotations.Nullable; import org.objectweb.asm.Opcodes; -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.JarIndex; 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.CachingClassProvider; import org.quiltmc.enigma.api.class_provider.ClassProvider; @@ -23,9 +18,6 @@ import org.quiltmc.enigma.api.class_provider.ObfuscationFixClassProvider; 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; @@ -39,8 +31,6 @@ 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; @@ -61,15 +51,11 @@ 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"; @@ -119,9 +105,9 @@ public static Builder builder() { public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, ProgressListener progress) throws IOException { JarClassProvider jarClassProvider = new JarClassProvider(path); - AbstractJarIndex jarIndex = MainJarIndex.empty(); - AbstractJarIndex libIndex = LibrariesJarIndex.empty(); - AbstractJarIndex comboIndex = CombinedJarIndex.empty(); + MainJarIndex jarIndex = MainJarIndex.empty(); + LibrariesJarIndex libIndex = LibrariesJarIndex.empty(); + JarIndex comboIndex = CombinedJarIndex.empty(jarIndex, libIndex); ClassLoaderClassProvider jreProvider = new ClassLoaderClassProvider(DriverManager.class.getClassLoader()); ClasspathClassProvider javaClassProvider = new ClasspathClassProvider(); @@ -130,16 +116,13 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog ProjectClassProvider projectClassProvider = new ProjectClassProvider(mainProjectProvider, librariesProvider); // main index - 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); + this.index(jarIndex, projectClassProvider, progress, "jar", false, jarIndex.getIndexableClassNames(projectClassProvider)); // lib index - this.index(libIndex, projectClassProvider, progress, "jar", true, mainReferencedPredicate); + this.index(libIndex, projectClassProvider, progress, "jar", true, libIndex.getIndexableClassNames(projectClassProvider)); // combined main and lib index - this.index(comboIndex, projectClassProvider, progress, "combined", true, mainReferencedPredicate); + this.index(comboIndex, projectClassProvider, progress, "combined", true, Collections.emptySet()); // name proposal var nameProposalServices = this.getNameProposalServices(); @@ -168,91 +151,27 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog return new EnigmaProject(this, path, mainProjectProvider, jarIndex, libIndex, comboIndex, mappingsIndex, proposedNames, Utils.zipSha1(path)); } - 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 + JarIndex index, ProjectClassProvider classProvider, ProgressListener progress, String progressKey, + boolean includesLibraries, Collection customIndexerScope ) { - 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 (!(includesLibraries && !service.shouldIndexLibraries())) { - progress.step(i++, I18n.translateFormatted("progress." + progressKey + ".custom_indexing.indexer", service.getId())); - Set scope = index.getIndexableClassNames(classProvider).stream() - .filter(classNameFilter) - .collect(Collectors.toCollection(HashSet::new)); - service.acceptJar(scope, classProvider, index); + index.indexJar(classProvider, progress); + + if (!customIndexerScope.isEmpty()) { + 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 (!(includesLibraries && !service.shouldIndexLibraries())) { + progress.step(i++, I18n.translateFormatted("progress." + progressKey + ".custom_indexing.indexer", service.getId())); + Set scope = new HashSet<>(customIndexerScope); + service.acceptJar(scope, classProvider, index); + } } - } - progress.step(i, I18n.translate("progress." + progressKey + ".custom_indexing.finished")); + progress.step(i, I18n.translate("progress." + progressKey + ".custom_indexing.finished")); + } } public EnigmaProfile getProfile() { diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java new file mode 100644 index 000000000..8f7da2860 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java @@ -0,0 +1,148 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import org.jspecify.annotations.Nullable; +import org.quiltmc.enigma.api.translation.mapping.EntryMapping; +import org.quiltmc.enigma.api.translation.mapping.tree.EntryTree; +import org.quiltmc.enigma.api.translation.representation.AccessFlags; +import org.quiltmc.enigma.api.translation.representation.entry.ClassDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.Entry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldEntry; +import org.quiltmc.enigma.api.translation.representation.entry.LocalVariableDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.LocalVariableEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; +import org.quiltmc.enigma.util.CombinedSet; + +import java.util.Collection; + +class CombinedEntryIndex implements EntryIndex { + private final EntryIndexImpl mainIndex; + private final EntryIndexImpl libIndex; + + private final Collection classes; + private final Collection methods; + private final Collection parameters; + private final Collection fields; + + CombinedEntryIndex(EntryIndexImpl mainIndex, EntryIndexImpl libIndex) { + this.mainIndex = mainIndex; + this.libIndex = libIndex; + + this.classes = new CombinedSet<>(this.mainIndex.getClasses(), this.libIndex.getClasses()); + this.methods = new CombinedSet<>(this.mainIndex.getMethods(), this.libIndex.getMethods()); + this.parameters = new CombinedSet<>(this.mainIndex.getParameters(), this.libIndex.getParameters()); + this.fields = new CombinedSet<>(this.mainIndex.getFields(), this.libIndex.getFields()); + } + + @Override + public boolean hasClass(ClassEntry entry) { + return this.mainIndex.hasClass(entry) || this.libIndex.hasClass(entry); + } + + @Override + public boolean hasMethod(MethodEntry entry) { + return this.mainIndex.hasMethod(entry) || this.libIndex.hasMethod(entry); + } + + @Override + public boolean hasParameter(LocalVariableEntry entry) { + return this.mainIndex.hasParameter(entry) || this.libIndex.hasParameter(entry); + } + + @Override + public boolean hasField(FieldEntry entry) { + return this.mainIndex.hasField(entry) || this.libIndex.hasField(entry); + } + + @Override + public boolean hasEntry(Entry entry) { + return this.mainIndex.hasEntry(entry) || this.libIndex.hasEntry(entry); + } + + @Override + public boolean validateParameterIndex(LocalVariableEntry parameter) { + return this.mainIndex.validateParameterIndex(parameter) || this.libIndex.validateParameterIndex(parameter); + } + + @Override + public @Nullable AccessFlags getMethodAccess(MethodEntry entry) { + final AccessFlags mainAccess = this.mainIndex.getMethodAccess(entry); + return mainAccess == null ? this.libIndex.getMethodAccess(entry) : mainAccess; + } + + @Override + public @Nullable AccessFlags getParameterAccess(LocalVariableEntry entry) { + final AccessFlags mainAccess = this.mainIndex.getParameterAccess(entry); + return mainAccess == null ? this.libIndex.getParameterAccess(entry) : mainAccess; + } + + @Override + public @Nullable AccessFlags getFieldAccess(FieldEntry entry) { + final AccessFlags mainAccess = this.mainIndex.getFieldAccess(entry); + return mainAccess == null ? this.libIndex.getFieldAccess(entry) : mainAccess; + } + + @Override + public @Nullable AccessFlags getClassAccess(ClassEntry entry) { + final AccessFlags mainAccess = this.mainIndex.getClassAccess(entry); + return mainAccess == null ? this.libIndex.getClassAccess(entry) : mainAccess; + } + + @Override + public @Nullable AccessFlags getEntryAccess(Entry entry) { + final AccessFlags mainAccess = this.mainIndex.getEntryAccess(entry); + return mainAccess == null ? this.libIndex.getEntryAccess(entry) : mainAccess; + } + + @Override + public @Nullable ClassDefEntry getDefinition(ClassEntry entry) { + final ClassDefEntry mainDef = this.mainIndex.getDefinition(entry); + return mainDef == null ? this.libIndex.getDefinition(entry) : mainDef; + } + + @Override + public @Nullable MethodDefEntry getDefinition(MethodEntry entry) { + final MethodDefEntry mainDef = this.mainIndex.getDefinition(entry); + return mainDef == null ? this.libIndex.getDefinition(entry) : mainDef; + } + + @Override + public @Nullable LocalVariableDefEntry getDefinition(LocalVariableEntry entry) { + final LocalVariableDefEntry mainDef = this.mainIndex.getDefinition(entry); + return mainDef == null ? this.libIndex.getDefinition(entry) : mainDef; + } + + @Override + public @Nullable FieldDefEntry getDefinition(FieldEntry entry) { + final FieldDefEntry mainDef = this.mainIndex.getDefinition(entry); + return mainDef == null ? this.libIndex.getDefinition(entry) : mainDef; + } + + @Override + public Collection getClasses() { + return this.classes; + } + + @Override + public Collection getMethods() { + return this.methods; + } + + @Override + public Collection getParameters() { + return this.parameters; + } + + @Override + public Collection getFields() { + return this.fields; + } + + @Override + public EntryTree getTree() { + // TODO + throw new UnsupportedOperationException(); + } +} 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 index e9dcd2dd0..7a169b2b2 100644 --- 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 @@ -21,8 +21,8 @@ public CombinedJarIndex( * Creates an empty index, configured to use all built-in indexers. * @return the newly created index */ - public static CombinedJarIndex empty() { - EntryIndex entryIndex = new EntryIndex(); + public static CombinedJarIndex empty(MainJarIndex mainIndex, LibrariesJarIndex libIndex) { + EntryIndex entryIndex = new CombinedEntryIndex(mainIndex.entryIndex, libIndex.entryIndex); ReferenceIndex referenceIndex = new ReferenceIndex(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); LambdaIndex lambdaIndex = new LambdaIndex(); diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java index ddb7e4b84..385e725f8 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java @@ -4,7 +4,6 @@ import org.quiltmc.enigma.api.EnigmaProject; import org.quiltmc.enigma.api.translation.mapping.EntryMapping; 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.AccessFlags; import org.quiltmc.enigma.api.translation.representation.entry.ClassDefEntry; import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; @@ -17,69 +16,15 @@ import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -public class EntryIndex implements JarIndexer { - private final EntryTree tree = new HashEntryTree<>(); +public interface EntryIndex extends JarIndexer { + boolean hasClass(ClassEntry entry); - private final Map fieldDefinitions = new HashMap<>(); - private final Map methodDefinitions = new HashMap<>(); - private final Map classDefinitions = new HashMap<>(); - private final Map parameterDefinitions = new HashMap<>(); + boolean hasMethod(MethodEntry entry); - @Override - public void indexClass(ClassDefEntry classEntry) { - this.classDefinitions.put(classEntry, classEntry); - } - - @Override - public void indexMethod(MethodDefEntry methodEntry) { - this.methodDefinitions.put(methodEntry, methodEntry); - methodEntry.streamParameters(this).forEach(paramEntry -> - this.parameterDefinitions.put(paramEntry, paramEntry) - ); - } + boolean hasParameter(LocalVariableEntry entry); - @Override - public void indexField(FieldDefEntry fieldEntry) { - this.fieldDefinitions.put(fieldEntry, fieldEntry); - } - - @Override - public void processIndex(JarIndex index) { - for (ClassEntry entry : this.getClasses()) { - this.tree.insert(entry, null); - } - - for (FieldEntry entry : this.getFields()) { - this.tree.insert(entry, null); - } - - for (MethodEntry entry : this.getMethods()) { - this.tree.insert(entry, null); - } - - for (LocalVariableEntry entry : this.getParameters()) { - this.tree.insert(entry, null); - } - } - - public boolean hasClass(ClassEntry entry) { - return this.classDefinitions.containsKey(entry); - } - - public boolean hasMethod(MethodEntry entry) { - return this.methodDefinitions.containsKey(entry); - } - - public boolean hasParameter(LocalVariableEntry entry) { - return this.parameterDefinitions.containsKey(entry); - } - - public boolean hasField(FieldEntry entry) { - return this.fieldDefinitions.containsKey(entry); - } + boolean hasField(FieldEntry entry); /** * Checks whether the entry has been indexed and therefore exists in the JAR file. @@ -90,21 +35,7 @@ public boolean hasField(FieldEntry entry) { * @param entry the entry to check * @return whether the entry exists */ - public boolean hasEntry(Entry entry) { - if (entry instanceof ClassEntry classEntry) { - return this.hasClass(classEntry); - } else if (entry instanceof MethodEntry methodEntry) { - return this.hasMethod(methodEntry); - } else if (entry instanceof FieldEntry fieldEntry) { - return this.hasField(fieldEntry); - } else if (entry instanceof LocalVariableEntry localVariableEntry) { - if (this.hasParameter(localVariableEntry)) { - return this.validateParameterIndex(localVariableEntry); - } - } - - return false; - } + boolean hasEntry(Entry entry); /** * Validates that the parameter index is not below the minimum index for its parent method and therefore could be valid. @@ -114,100 +45,58 @@ public boolean hasEntry(Entry entry) { * @return whether the index could be valid * @see EnigmaProject#validateParameterIndex(LocalVariableEntry) */ - public boolean validateParameterIndex(LocalVariableEntry parameter) { - MethodEntry parent = parameter.getParent(); - AccessFlags parentAccess = this.getMethodAccess(parent); - - int startIndex = parentAccess != null && parentAccess.isStatic() ? 0 : 1; - return parameter.getIndex() >= startIndex; - } + boolean validateParameterIndex(LocalVariableEntry parameter); @Nullable - public AccessFlags getMethodAccess(MethodEntry entry) { - var def = this.methodDefinitions.get(entry); - return def == null ? null : def.getAccess(); - } + AccessFlags getMethodAccess(MethodEntry entry); @Nullable - public AccessFlags getParameterAccess(LocalVariableEntry entry) { - var def = this.parameterDefinitions.get(entry); - return def == null ? null : this.getMethodAccess(def.getParent()); - } + AccessFlags getParameterAccess(LocalVariableEntry entry); @Nullable - public AccessFlags getFieldAccess(FieldEntry entry) { - var def = this.fieldDefinitions.get(entry); - return def == null ? null : def.getAccess(); - } + AccessFlags getFieldAccess(FieldEntry entry); @Nullable - public AccessFlags getClassAccess(ClassEntry entry) { - var def = this.classDefinitions.get(entry); - return def == null ? null : def.getAccess(); - } + AccessFlags getClassAccess(ClassEntry entry); @Nullable - public AccessFlags getEntryAccess(Entry entry) { - if (entry instanceof MethodEntry methodEntry) { - return this.getMethodAccess(methodEntry); - } else if (entry instanceof FieldEntry fieldEntry) { - return this.getFieldAccess(fieldEntry); - } else if (entry instanceof LocalVariableEntry localVariableEntry) { - return this.getParameterAccess(localVariableEntry); - } else if (entry instanceof ClassEntry classEntry) { - return this.getClassAccess(classEntry); - } - - return null; - } + AccessFlags getEntryAccess(Entry entry); @Nullable - public ClassDefEntry getDefinition(ClassEntry entry) { - return this.classDefinitions.get(entry); - } + ClassDefEntry getDefinition(ClassEntry entry); @Nullable - public MethodDefEntry getDefinition(MethodEntry entry) { - return this.methodDefinitions.get(entry); - } + MethodDefEntry getDefinition(MethodEntry entry); @Nullable - public LocalVariableDefEntry getDefinition(LocalVariableEntry entry) { - return this.parameterDefinitions.get(entry); - } + LocalVariableDefEntry getDefinition(LocalVariableEntry entry); @Nullable - public FieldDefEntry getDefinition(FieldEntry entry) { - return this.fieldDefinitions.get(entry); - } + FieldDefEntry getDefinition(FieldEntry entry); - public Collection getClasses() { - return this.classDefinitions.keySet(); - } + Collection getClasses(); - public Collection getMethods() { - return this.methodDefinitions.keySet(); - } + Collection getMethods(); - public Collection getParameters() { - return this.parameterDefinitions.keySet(); - } + Collection getParameters(); - public Collection getFields() { - return this.fieldDefinitions.keySet(); - } + Collection getFields(); /** * Returns all indexed entries, organised into an {@link EntryTree}. * Note that all entries will have their mapping set to {@code null}. + * * @return the entry tree */ - public EntryTree getTree() { - return this.tree; + EntryTree getTree(); + + @Override + default Class getType() { + return EntryIndex.class; } @Override - public String getTranslationKey() { + default String getTranslationKey() { return "progress.jar.indexing.process.entries"; } } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java new file mode 100644 index 000000000..1a6b98f72 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java @@ -0,0 +1,196 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import org.jspecify.annotations.Nullable; +import org.quiltmc.enigma.api.translation.mapping.EntryMapping; +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.AccessFlags; +import org.quiltmc.enigma.api.translation.representation.entry.ClassDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.Entry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldEntry; +import org.quiltmc.enigma.api.translation.representation.entry.LocalVariableDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.LocalVariableEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +class EntryIndexImpl implements EntryIndex { + private final EntryTree tree = new HashEntryTree<>(); + + private final Map fieldDefinitions = new HashMap<>(); + private final Map methodDefinitions = new HashMap<>(); + private final Map classDefinitions = new HashMap<>(); + private final Map parameterDefinitions = new HashMap<>(); + + @Override + public void indexClass(ClassDefEntry classEntry) { + this.classDefinitions.put(classEntry, classEntry); + } + + @Override + public void indexMethod(MethodDefEntry methodEntry) { + this.methodDefinitions.put(methodEntry, methodEntry); + methodEntry.streamParameters(this).forEach(paramEntry -> + this.parameterDefinitions.put(paramEntry, paramEntry) + ); + } + + @Override + public void indexField(FieldDefEntry fieldEntry) { + this.fieldDefinitions.put(fieldEntry, fieldEntry); + } + + @Override + public void processIndex(JarIndex index) { + for (ClassEntry entry : this.getClasses()) { + this.tree.insert(entry, null); + } + + for (FieldEntry entry : this.getFields()) { + this.tree.insert(entry, null); + } + + for (MethodEntry entry : this.getMethods()) { + this.tree.insert(entry, null); + } + + for (LocalVariableEntry entry : this.getParameters()) { + this.tree.insert(entry, null); + } + } + + @Override + public boolean hasClass(ClassEntry entry) { + return this.classDefinitions.containsKey(entry); + } + + @Override + public boolean hasMethod(MethodEntry entry) { + return this.methodDefinitions.containsKey(entry); + } + + @Override + public boolean hasParameter(LocalVariableEntry entry) { + return this.parameterDefinitions.containsKey(entry); + } + + @Override + public boolean hasField(FieldEntry entry) { + return this.fieldDefinitions.containsKey(entry); + } + + @Override + public boolean hasEntry(Entry entry) { + if (entry instanceof ClassEntry classEntry) { + return this.hasClass(classEntry); + } else if (entry instanceof MethodEntry methodEntry) { + return this.hasMethod(methodEntry); + } else if (entry instanceof FieldEntry fieldEntry) { + return this.hasField(fieldEntry); + } else if (entry instanceof LocalVariableEntry localVariableEntry) { + if (this.hasParameter(localVariableEntry)) { + return this.validateParameterIndex(localVariableEntry); + } + } + + return false; + } + + @Override + public boolean validateParameterIndex(LocalVariableEntry parameter) { + MethodEntry parent = parameter.getParent(); + AccessFlags parentAccess = this.getMethodAccess(parent); + + int startIndex = parentAccess != null && parentAccess.isStatic() ? 0 : 1; + return parameter.getIndex() >= startIndex; + } + + @Override + public @Nullable AccessFlags getMethodAccess(MethodEntry entry) { + var def = this.methodDefinitions.get(entry); + return def == null ? null : def.getAccess(); + } + + @Override + public @Nullable AccessFlags getParameterAccess(LocalVariableEntry entry) { + var def = this.parameterDefinitions.get(entry); + return def == null ? null : this.getMethodAccess(def.getParent()); + } + + @Override + public @Nullable AccessFlags getFieldAccess(FieldEntry entry) { + var def = this.fieldDefinitions.get(entry); + return def == null ? null : def.getAccess(); + } + + @Override + public @Nullable AccessFlags getClassAccess(ClassEntry entry) { + var def = this.classDefinitions.get(entry); + return def == null ? null : def.getAccess(); + } + + @Override + public @Nullable AccessFlags getEntryAccess(Entry entry) { + if (entry instanceof MethodEntry methodEntry) { + return this.getMethodAccess(methodEntry); + } else if (entry instanceof FieldEntry fieldEntry) { + return this.getFieldAccess(fieldEntry); + } else if (entry instanceof LocalVariableEntry localVariableEntry) { + return this.getParameterAccess(localVariableEntry); + } else if (entry instanceof ClassEntry classEntry) { + return this.getClassAccess(classEntry); + } + + return null; + } + + @Override + public @Nullable ClassDefEntry getDefinition(ClassEntry entry) { + return this.classDefinitions.get(entry); + } + + @Override + public @Nullable MethodDefEntry getDefinition(MethodEntry entry) { + return this.methodDefinitions.get(entry); + } + + @Override + public @Nullable LocalVariableDefEntry getDefinition(LocalVariableEntry entry) { + return this.parameterDefinitions.get(entry); + } + + @Override + public @Nullable FieldDefEntry getDefinition(FieldEntry entry) { + return this.fieldDefinitions.get(entry); + } + + @Override + public Set getClasses() { + return this.classDefinitions.keySet(); + } + + @Override + public Set getMethods() { + return this.methodDefinitions.keySet(); + } + + @Override + public Set getParameters() { + return this.parameterDefinitions.keySet(); + } + + @Override + public Set getFields() { + return this.fieldDefinitions.keySet(); + } + + @Override + public EntryTree getTree() { + return this.tree; + } +} diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/JarIndexer.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/JarIndexer.java index 37581fbb6..ce8e73710 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/JarIndexer.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/JarIndexer.java @@ -39,6 +39,18 @@ default void processIndex(JarIndex index) { String getTranslationKey(); + // TODO we should probably replace this with a type object in a breaking update + /** + * @return the class used to {@linkplain JarIndex#getIndex(Class) look up} this indexer in a {@link JarIndex} + * + * @implSpec must return a type to which this indexer is assignable + * + * @implNote a {@link JarIndex} can only contain one index of a given type + */ + default Class getType() { + return this.getClass(); + } + record EnclosingMethodData(String owner, String name, String descriptor) { public MethodEntry getMethod() { return MethodEntry.parse(this.owner, this.name, this.descriptor); 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 7c998853e..0d1edb895 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 @@ -10,11 +10,14 @@ * An index of the library jars of an {@link EnigmaProject}. */ public class LibrariesJarIndex extends AbstractJarIndex { + final EntryIndexImpl entryIndex; + public LibrariesJarIndex( - EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + EntryIndexImpl entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); + this.entryIndex = entryIndex; } /** @@ -22,7 +25,7 @@ public LibrariesJarIndex( * @return the newly created index */ public static LibrariesJarIndex empty() { - EntryIndex entryIndex = new EntryIndex(); + EntryIndexImpl entryIndex = new EntryIndexImpl(); ReferenceIndex referenceIndex = new ReferenceIndex(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); return new LibrariesJarIndex( 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 6b0cd5ba9..4784ea9ef 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 @@ -10,11 +10,14 @@ * An index of the main jar of an {@link EnigmaProject}. */ public class MainJarIndex extends AbstractJarIndex { + final EntryIndexImpl entryIndex; + public MainJarIndex( - EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + EntryIndexImpl entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); + this.entryIndex = entryIndex; } /** @@ -22,7 +25,7 @@ public MainJarIndex( * @return the newly created index */ public static MainJarIndex empty() { - EntryIndex entryIndex = new EntryIndex(); + EntryIndexImpl entryIndex = new EntryIndexImpl(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); ReferenceIndex referenceIndex = new ReferenceIndex(); BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex); 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 893ec9efd..2db12a060 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 @@ -67,7 +67,10 @@ public AbstractJarIndex( Stream.of(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex), Arrays.stream(otherIndexers) ) - .collect(ImmutableMap.toImmutableMap(JarIndexer::getClass, Function.identity())); + .collect(ImmutableMap.toImmutableMap( + JarIndexer::getType, + Function.identity() + )); this.entryResolver = new IndexEntryResolver(this); this.childrenByClass = ArrayListMultimap.create(); diff --git a/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java new file mode 100644 index 000000000..c2b0830c1 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java @@ -0,0 +1,165 @@ +package org.quiltmc.enigma.util; + +import org.jspecify.annotations.NonNull; + +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * A set backed by two other sets. + * + *

Does not support the {@link #add(Object)} or {@link #addAll(Collection)} methods! + * + *

Removal is supported. + * + * @param the type of elements + */ +public class CombinedSet implements Set { + private static UnsupportedOperationException createUnsupportedAddException() { + return new UnsupportedOperationException("cannot add to combined set!"); + } + + private final Set first; + private final Set second; + + public CombinedSet(Set first, Set second) { + this.first = first; + this.second = second; + } + + @Override + public int size() { + return this.first.size() + this.second.size(); + } + + @Override + public boolean isEmpty() { + return this.first.isEmpty() && this.second.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.first.contains(o) || this.second.contains(o); + } + + @NonNull + @Override + public Iterator iterator() { + return new CombinedIterator(); + } + + @Override + public Object @NonNull[] toArray() { + final int size = this.size(); + final Object[] array = new Object[size]; + final Iterator itr = this.iterator(); + int i = 0; + while (itr.hasNext()) { + array[i++] = itr.next(); + } + + return array; + } + + // Based on HashMap::prepareArray and HashMap::keysToArray + @SuppressWarnings("unchecked") + @Override + public T1 @NonNull[] toArray(T1[] a) { + final int size = this.size(); + if (a.length < size) { + a = (T1[]) java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), size); + } else if (a.length > size) { + a[size] = null; + } + + final Iterator itr = this.iterator(); + final Object[] objects = a; + for (int i = 0; i < size; i++) { + objects[i] = itr.next(); + } + + return a; + } + + @Override + public boolean add(T t) { + throw createUnsupportedAddException(); + } + + @Override + public boolean remove(Object o) { + return this.first.remove(o) || this.second.remove(o); + } + + @SuppressWarnings("SuspiciousMethodCalls") + @Override + public boolean containsAll(@NonNull Collection collection) { + return collection.stream().allMatch(o -> this.first.contains(o) || this.second.contains(o)); + } + + @Override + public boolean addAll(@NonNull Collection collection) { + throw createUnsupportedAddException(); + } + + @Override + public boolean retainAll(@NonNull Collection collection) { + final boolean firstChanged = this.first.retainAll(collection); + final boolean secondChanged = this.second.retainAll(collection); + return firstChanged || secondChanged; + } + + @Override + public boolean removeAll(@NonNull Collection collection) { + return this.first.removeAll(collection) || this.second.removeAll(collection); + } + + @Override + public void clear() { + this.first.clear(); + this.second.clear(); + } + + private class CombinedIterator implements Iterator { + Iterator delegate = CombinedSet.this.first.iterator(); + + boolean iteratingFirst = true; + + @Override + public boolean hasNext() { + final boolean currentHasNext = this.delegate.hasNext(); + if (currentHasNext) { + return true; + } else { + if (this.iteratingFirst) { + this.delegate = CombinedSet.this.second.iterator(); + this.iteratingFirst = false; + + return this.delegate.hasNext(); + } else { + return false; + } + } + } + + @Override + public T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } + + return this.delegate.next(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void remove() { + // update delegate + this.hasNext(); + this.delegate.remove(); + } + } +} From f29bb31f8e180bccddadccb30b6d04047a175fd8 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 14:21:43 -0800 Subject: [PATCH 02/14] refactored CombinedSet -> CombinedCollection make CombinedJarIndex's ReferenceIndex a combination of MainJarIndex's and LibrariesJarIndex's; does NOT currently index references from main to lib --- .../index/jar/CombinedEntryIndex.java | 16 +- .../analysis/index/jar/CombinedJarIndex.java | 5 +- .../index/jar/CombinedReferenceIndex.java | 80 ++++++++ .../analysis/index/jar/EntryIndexImpl.java | 10 +- .../analysis/index/jar/LibrariesJarIndex.java | 8 +- .../api/analysis/index/jar/MainJarIndex.java | 8 +- .../analysis/index/jar/ReferenceIndex.java | 159 ++-------------- .../index/jar/ReferenceIndexImpl.java | 175 ++++++++++++++++++ ...mbinedSet.java => CombinedCollection.java} | 25 ++- 9 files changed, 304 insertions(+), 182 deletions(-) create mode 100644 enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java create mode 100644 enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndexImpl.java rename enigma/src/main/java/org/quiltmc/enigma/util/{CombinedSet.java => CombinedCollection.java} (82%) diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java index 8f7da2860..edd874be2 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java @@ -13,27 +13,27 @@ import org.quiltmc.enigma.api.translation.representation.entry.LocalVariableEntry; import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; -import org.quiltmc.enigma.util.CombinedSet; +import org.quiltmc.enigma.util.CombinedCollection; import java.util.Collection; class CombinedEntryIndex implements EntryIndex { - private final EntryIndexImpl mainIndex; - private final EntryIndexImpl libIndex; + private final EntryIndex mainIndex; + private final EntryIndex libIndex; private final Collection classes; private final Collection methods; private final Collection parameters; private final Collection fields; - CombinedEntryIndex(EntryIndexImpl mainIndex, EntryIndexImpl libIndex) { + CombinedEntryIndex(EntryIndex mainIndex, EntryIndex libIndex) { this.mainIndex = mainIndex; this.libIndex = libIndex; - this.classes = new CombinedSet<>(this.mainIndex.getClasses(), this.libIndex.getClasses()); - this.methods = new CombinedSet<>(this.mainIndex.getMethods(), this.libIndex.getMethods()); - this.parameters = new CombinedSet<>(this.mainIndex.getParameters(), this.libIndex.getParameters()); - this.fields = new CombinedSet<>(this.mainIndex.getFields(), this.libIndex.getFields()); + this.classes = new CombinedCollection<>(this.mainIndex.getClasses(), this.libIndex.getClasses()); + this.methods = new CombinedCollection<>(this.mainIndex.getMethods(), this.libIndex.getMethods()); + this.parameters = new CombinedCollection<>(this.mainIndex.getParameters(), this.libIndex.getParameters()); + this.fields = new CombinedCollection<>(this.mainIndex.getFields(), this.libIndex.getFields()); } @Override 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 index 7a169b2b2..0064007f6 100644 --- 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 @@ -8,6 +8,9 @@ /** * An index of the main jar and library jars of an {@link EnigmaProject}. + * + *

Note: currently the contained {@link ReferenceIndex} does not index main jar references to + * library types and members. */ public class CombinedJarIndex extends AbstractJarIndex { public CombinedJarIndex( @@ -23,7 +26,7 @@ public CombinedJarIndex( */ public static CombinedJarIndex empty(MainJarIndex mainIndex, LibrariesJarIndex libIndex) { EntryIndex entryIndex = new CombinedEntryIndex(mainIndex.entryIndex, libIndex.entryIndex); - ReferenceIndex referenceIndex = new ReferenceIndex(); + ReferenceIndex referenceIndex = new CombinedReferenceIndex(mainIndex.referenceIndex, libIndex.referenceIndex); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); LambdaIndex lambdaIndex = new LambdaIndex(); return new CombinedJarIndex( diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java new file mode 100644 index 000000000..d63dba2f6 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java @@ -0,0 +1,80 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import org.quiltmc.enigma.api.analysis.EntryReference; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; +import org.quiltmc.enigma.util.CombinedCollection; + +import java.util.Collection; + +/** + * Note: does not currently index main jar references to library types and members. + */ +class CombinedReferenceIndex implements ReferenceIndex { + private final ReferenceIndex mainIndex; + private final ReferenceIndex libIndex; + + CombinedReferenceIndex(ReferenceIndex mainIndex, ReferenceIndex libIndex) { + this.mainIndex = mainIndex; + this.libIndex = libIndex; + } + + @Override + public Collection getMethodsReferencedBy(MethodEntry entry) { + return new CombinedCollection<>( + this.mainIndex.getMethodsReferencedBy(entry), + this.libIndex.getMethodsReferencedBy(entry) + ); + } + + @Override + public Collection getFieldsReferencedBy(MethodEntry entry) { + return new CombinedCollection<>( + this.mainIndex.getFieldsReferencedBy(entry), + this.libIndex.getFieldsReferencedBy(entry) + ); + } + + @Override + public Collection> getReferencesToField(FieldEntry entry) { + return new CombinedCollection<>( + this.mainIndex.getReferencesToField(entry), + this.libIndex.getReferencesToField(entry) + ); + } + + @Override + public Collection> getReferencesToClass(ClassEntry entry) { + return new CombinedCollection<>( + this.mainIndex.getReferencesToClass(entry), + this.libIndex.getReferencesToClass(entry) + ); + } + + @Override + public Collection> getReferencesToMethod(MethodEntry entry) { + return new CombinedCollection<>( + this.mainIndex.getReferencesToMethod(entry), + this.libIndex.getReferencesToMethod(entry) + ); + } + + @Override + public Collection> getFieldTypeReferencesToClass(ClassEntry entry) { + return new CombinedCollection<>( + this.mainIndex.getFieldTypeReferencesToClass(entry), + this.libIndex.getFieldTypeReferencesToClass(entry) + ); + } + + @Override + public Collection> getMethodTypeReferencesToClass(ClassEntry entry) { + return new CombinedCollection<>( + this.mainIndex.getMethodTypeReferencesToClass(entry), + this.libIndex.getMethodTypeReferencesToClass(entry) + ); + } +} diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java index 1a6b98f72..af624360b 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java @@ -15,9 +15,9 @@ import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; +import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Set; class EntryIndexImpl implements EntryIndex { private final EntryTree tree = new HashEntryTree<>(); @@ -170,22 +170,22 @@ public boolean validateParameterIndex(LocalVariableEntry parameter) { } @Override - public Set getClasses() { + public Collection getClasses() { return this.classDefinitions.keySet(); } @Override - public Set getMethods() { + public Collection getMethods() { return this.methodDefinitions.keySet(); } @Override - public Set getParameters() { + public Collection getParameters() { return this.parameterDefinitions.keySet(); } @Override - public Set getFields() { + public Collection getFields() { return this.fieldDefinitions.keySet(); } 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 0d1edb895..12b39218e 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 @@ -9,15 +9,17 @@ /** * An index of the library jars of an {@link EnigmaProject}. */ -public class LibrariesJarIndex extends AbstractJarIndex { +public final class LibrariesJarIndex extends AbstractJarIndex { final EntryIndexImpl entryIndex; + final ReferenceIndex referenceIndex; - public LibrariesJarIndex( + private LibrariesJarIndex( EntryIndexImpl entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); this.entryIndex = entryIndex; + this.referenceIndex = referenceIndex; } /** @@ -26,7 +28,7 @@ public LibrariesJarIndex( */ public static LibrariesJarIndex empty() { EntryIndexImpl entryIndex = new EntryIndexImpl(); - ReferenceIndex referenceIndex = new ReferenceIndex(); + ReferenceIndex referenceIndex = new ReferenceIndexImpl(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); return new LibrariesJarIndex( entryIndex, inheritanceIndex, referenceIndex, 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 4784ea9ef..bf3068801 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 @@ -9,15 +9,17 @@ /** * An index of the main jar of an {@link EnigmaProject}. */ -public class MainJarIndex extends AbstractJarIndex { +public final class MainJarIndex extends AbstractJarIndex { final EntryIndexImpl entryIndex; + final ReferenceIndex referenceIndex; - public MainJarIndex( + private MainJarIndex( EntryIndexImpl entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); this.entryIndex = entryIndex; + this.referenceIndex = referenceIndex; } /** @@ -27,7 +29,7 @@ public MainJarIndex( public static MainJarIndex empty() { EntryIndexImpl entryIndex = new EntryIndexImpl(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); - ReferenceIndex referenceIndex = new ReferenceIndex(); + ReferenceIndex referenceIndex = new ReferenceIndexImpl(); BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex); PackageVisibilityIndex packageVisibilityIndex = new PackageVisibilityIndex(); EnclosingMethodIndex enclosingMethodIndex = new EnclosingMethodIndex(); 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 c81e488fb..6575c26b2 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 @@ -1,173 +1,36 @@ package org.quiltmc.enigma.api.analysis.index.jar; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import org.quiltmc.enigma.api.analysis.EntryReference; -import org.quiltmc.enigma.api.analysis.ReferenceTargetType; -import org.quiltmc.enigma.api.translation.mapping.ResolutionStrategy; -import org.quiltmc.enigma.api.translation.representation.Lambda; -import org.quiltmc.enigma.api.translation.representation.MethodDescriptor; -import org.quiltmc.enigma.api.translation.representation.TypeDescriptor; import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; -import org.quiltmc.enigma.api.translation.representation.entry.Entry; import org.quiltmc.enigma.api.translation.representation.entry.FieldDefEntry; import org.quiltmc.enigma.api.translation.representation.entry.FieldEntry; import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; import java.util.Collection; -import java.util.Map; -public class ReferenceIndex implements JarIndexer { - private Multimap methodReferences = HashMultimap.create(); - private Multimap fieldReferences = HashMultimap.create(); +public interface ReferenceIndex extends JarIndexer { + Collection getMethodsReferencedBy(MethodEntry entry); - private Multimap> referencesToMethods = HashMultimap.create(); - private Multimap> referencesToClasses = HashMultimap.create(); - private Multimap> referencesToFields = HashMultimap.create(); - private Multimap> fieldTypeReferences = HashMultimap.create(); - private Multimap> methodTypeReferences = HashMultimap.create(); + Collection getFieldsReferencedBy(MethodEntry entry); - @Override - public void indexMethod(MethodDefEntry methodEntry) { - this.indexMethodDescriptor(methodEntry, methodEntry.getDesc()); - } - - private void indexMethodDescriptor(MethodDefEntry entry, MethodDescriptor descriptor) { - for (TypeDescriptor typeDescriptor : descriptor.getArgumentDescs()) { - this.indexMethodTypeDescriptor(entry, typeDescriptor); - } + Collection> getReferencesToField(FieldEntry entry); - this.indexMethodTypeDescriptor(entry, descriptor.getReturnDesc()); - } - - private void indexMethodTypeDescriptor(MethodDefEntry method, TypeDescriptor typeDescriptor) { - if (typeDescriptor.isType()) { - ClassEntry referencedClass = typeDescriptor.getTypeEntry(); - this.methodTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), method)); - } else if (typeDescriptor.isArray()) { - this.indexMethodTypeDescriptor(method, typeDescriptor.getArrayType()); - } - } + Collection> getReferencesToClass(ClassEntry entry); - @Override - public void indexField(FieldDefEntry fieldEntry) { - this.indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc()); - } + Collection> getReferencesToMethod(MethodEntry entry); - private void indexFieldTypeDescriptor(FieldDefEntry field, TypeDescriptor typeDescriptor) { - if (typeDescriptor.isType()) { - ClassEntry referencedClass = typeDescriptor.getTypeEntry(); - this.fieldTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), field)); - } else if (typeDescriptor.isArray()) { - this.indexFieldTypeDescriptor(field, typeDescriptor.getArrayType()); - } - } + Collection> getFieldTypeReferencesToClass(ClassEntry entry); - @Override - public void indexClassReference(MethodDefEntry callerEntry, ClassEntry referencedEntry, ReferenceTargetType targetType) { - this.referencesToClasses.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); - } + Collection> getMethodTypeReferencesToClass(ClassEntry entry); @Override - public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { - this.referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); - this.methodReferences.put(callerEntry, referencedEntry); - - if (referencedEntry.isConstructor()) { - ClassEntry referencedClass = referencedEntry.getParent(); - this.referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry, targetType)); - } - } - - @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 - public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { - if (lambda.implMethod() instanceof MethodEntry method) { - this.indexMethodReference(callerEntry, method, targetType); - } else { - this.indexFieldReference(callerEntry, (FieldEntry) lambda.implMethod(), targetType); - } - - this.indexMethodDescriptor(callerEntry, lambda.invokedType()); - this.indexMethodDescriptor(callerEntry, lambda.samMethodType()); - this.indexMethodDescriptor(callerEntry, lambda.instantiatedMethodType()); - } - - @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); - this.fieldTypeReferences = this.remapReferencesTo(index, this.fieldTypeReferences); - this.methodTypeReferences = this.remapReferencesTo(index, this.methodTypeReferences); - } - - private , V extends Entry> Multimap remapReferences(JarIndex index, Multimap multimap) { - final int keySetSize = multimap.keySet().size(); - Multimap resolved = HashMultimap.create(multimap.keySet().size(), keySetSize == 0 ? 0 : multimap.size() / keySetSize); - for (Map.Entry entry : multimap.entries()) { - resolved.put(this.remap(index, entry.getKey()), this.remap(index, entry.getValue())); - } - - return resolved; - } - - private , C extends Entry> Multimap> remapReferencesTo(JarIndex index, Multimap> multimap) { - final int keySetSize = multimap.keySet().size(); - Multimap> resolved = HashMultimap.create(keySetSize, keySetSize == 0 ? 0 : multimap.size() / keySetSize); - for (Map.Entry> entry : multimap.entries()) { - resolved.put(this.remap(index, entry.getKey()), this.remap(index, entry.getValue())); - } - - return resolved; - } - - private > E remap(JarIndex index, E entry) { - return index.getEntryResolver().resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST); - } - - private , C extends Entry> EntryReference remap(JarIndex index, EntryReference reference) { - return index.getEntryResolver().resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); - } - - 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); - } - - public Collection> getReferencesToClass(ClassEntry entry) { - return this.referencesToClasses.get(entry); - } - - public Collection> getReferencesToMethod(MethodEntry entry) { - return this.referencesToMethods.get(entry); - } - - public Collection> getFieldTypeReferencesToClass(ClassEntry entry) { - return this.fieldTypeReferences.get(entry); - } - - public Collection> getMethodTypeReferencesToClass(ClassEntry entry) { - return this.methodTypeReferences.get(entry); + default Class getType() { + return ReferenceIndex.class; } @Override - public String getTranslationKey() { + default String getTranslationKey() { return "progress.jar.indexing.process.references"; } } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndexImpl.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndexImpl.java new file mode 100644 index 000000000..32147fa12 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndexImpl.java @@ -0,0 +1,175 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import org.quiltmc.enigma.api.analysis.EntryReference; +import org.quiltmc.enigma.api.analysis.ReferenceTargetType; +import org.quiltmc.enigma.api.translation.mapping.ResolutionStrategy; +import org.quiltmc.enigma.api.translation.representation.Lambda; +import org.quiltmc.enigma.api.translation.representation.MethodDescriptor; +import org.quiltmc.enigma.api.translation.representation.TypeDescriptor; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.Entry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.FieldEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; + +import java.util.Collection; +import java.util.Map; + +class ReferenceIndexImpl implements ReferenceIndex { + private Multimap methodReferences = HashMultimap.create(); + private Multimap fieldReferences = HashMultimap.create(); + + private Multimap> referencesToMethods = HashMultimap.create(); + private Multimap> referencesToClasses = HashMultimap.create(); + private Multimap> referencesToFields = HashMultimap.create(); + private Multimap> fieldTypeReferences = HashMultimap.create(); + private Multimap> methodTypeReferences = HashMultimap.create(); + + @Override + public void indexMethod(MethodDefEntry methodEntry) { + this.indexMethodDescriptor(methodEntry, methodEntry.getDesc()); + } + + private void indexMethodDescriptor(MethodDefEntry entry, MethodDescriptor descriptor) { + for (TypeDescriptor typeDescriptor : descriptor.getArgumentDescs()) { + this.indexMethodTypeDescriptor(entry, typeDescriptor); + } + + this.indexMethodTypeDescriptor(entry, descriptor.getReturnDesc()); + } + + private void indexMethodTypeDescriptor(MethodDefEntry method, TypeDescriptor typeDescriptor) { + if (typeDescriptor.isType()) { + ClassEntry referencedClass = typeDescriptor.getTypeEntry(); + this.methodTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), method)); + } else if (typeDescriptor.isArray()) { + this.indexMethodTypeDescriptor(method, typeDescriptor.getArrayType()); + } + } + + @Override + public void indexField(FieldDefEntry fieldEntry) { + this.indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc()); + } + + private void indexFieldTypeDescriptor(FieldDefEntry field, TypeDescriptor typeDescriptor) { + if (typeDescriptor.isType()) { + ClassEntry referencedClass = typeDescriptor.getTypeEntry(); + this.fieldTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), field)); + } else if (typeDescriptor.isArray()) { + this.indexFieldTypeDescriptor(field, typeDescriptor.getArrayType()); + } + } + + @Override + public void indexClassReference(MethodDefEntry callerEntry, ClassEntry referencedEntry, ReferenceTargetType targetType) { + this.referencesToClasses.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); + } + + @Override + public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry, ReferenceTargetType targetType) { + this.referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry, targetType)); + this.methodReferences.put(callerEntry, referencedEntry); + + if (referencedEntry.isConstructor()) { + ClassEntry referencedClass = referencedEntry.getParent(); + this.referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry, targetType)); + } + } + + @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 + public void indexLambda(MethodDefEntry callerEntry, Lambda lambda, ReferenceTargetType targetType) { + if (lambda.implMethod() instanceof MethodEntry method) { + this.indexMethodReference(callerEntry, method, targetType); + } else { + this.indexFieldReference(callerEntry, (FieldEntry) lambda.implMethod(), targetType); + } + + this.indexMethodDescriptor(callerEntry, lambda.invokedType()); + this.indexMethodDescriptor(callerEntry, lambda.samMethodType()); + this.indexMethodDescriptor(callerEntry, lambda.instantiatedMethodType()); + } + + @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); + this.fieldTypeReferences = this.remapReferencesTo(index, this.fieldTypeReferences); + this.methodTypeReferences = this.remapReferencesTo(index, this.methodTypeReferences); + } + + private , V extends Entry> Multimap remapReferences(JarIndex index, Multimap multimap) { + final int keySetSize = multimap.keySet().size(); + Multimap resolved = HashMultimap.create(multimap.keySet().size(), keySetSize == 0 ? 0 : multimap.size() / keySetSize); + for (Map.Entry entry : multimap.entries()) { + resolved.put(this.remap(index, entry.getKey()), this.remap(index, entry.getValue())); + } + + return resolved; + } + + private , C extends Entry> Multimap> remapReferencesTo(JarIndex index, Multimap> multimap) { + final int keySetSize = multimap.keySet().size(); + Multimap> resolved = HashMultimap.create(keySetSize, keySetSize == 0 ? 0 : multimap.size() / keySetSize); + for (Map.Entry> entry : multimap.entries()) { + resolved.put(this.remap(index, entry.getKey()), this.remap(index, entry.getValue())); + } + + return resolved; + } + + private > E remap(JarIndex index, E entry) { + return index.getEntryResolver().resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST); + } + + private , C extends Entry> EntryReference remap(JarIndex index, EntryReference reference) { + return index.getEntryResolver().resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); + } + + @Override + public Collection getMethodsReferencedBy(MethodEntry entry) { + return this.methodReferences.get(entry); + } + + @Override + public Collection getFieldsReferencedBy(MethodEntry entry) { + return this.fieldReferences.get(entry); + } + + @Override + public Collection> getReferencesToField(FieldEntry entry) { + return this.referencesToFields.get(entry); + } + + @Override + public Collection> getReferencesToClass(ClassEntry entry) { + return this.referencesToClasses.get(entry); + } + + @Override + public Collection> getReferencesToMethod(MethodEntry entry) { + return this.referencesToMethods.get(entry); + } + + @Override + public Collection> getFieldTypeReferencesToClass(ClassEntry entry) { + return this.fieldTypeReferences.get(entry); + } + + @Override + public Collection> getMethodTypeReferencesToClass(ClassEntry entry) { + return this.methodTypeReferences.get(entry); + } +} diff --git a/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedCollection.java similarity index 82% rename from enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java rename to enigma/src/main/java/org/quiltmc/enigma/util/CombinedCollection.java index c2b0830c1..80a6fdf43 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java +++ b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedCollection.java @@ -5,10 +5,9 @@ import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; -import java.util.Set; /** - * A set backed by two other sets. + * A collection backed by two other collections. * *

Does not support the {@link #add(Object)} or {@link #addAll(Collection)} methods! * @@ -16,15 +15,15 @@ * * @param the type of elements */ -public class CombinedSet implements Set { +public class CombinedCollection implements Collection { private static UnsupportedOperationException createUnsupportedAddException() { - return new UnsupportedOperationException("cannot add to combined set!"); + return new UnsupportedOperationException("cannot add to combined collection!"); } - private final Set first; - private final Set second; + private final Collection first; + private final Collection second; - public CombinedSet(Set first, Set second) { + public CombinedCollection(Collection first, Collection second) { this.first = first; this.second = second; } @@ -91,7 +90,7 @@ public boolean add(T t) { @Override public boolean remove(Object o) { - return this.first.remove(o) || this.second.remove(o); + return this.first.remove(o) | this.second.remove(o); } @SuppressWarnings("SuspiciousMethodCalls") @@ -107,14 +106,12 @@ public boolean addAll(@NonNull Collection collection) { @Override public boolean retainAll(@NonNull Collection collection) { - final boolean firstChanged = this.first.retainAll(collection); - final boolean secondChanged = this.second.retainAll(collection); - return firstChanged || secondChanged; + return this.first.retainAll(collection) | this.second.retainAll(collection); } @Override public boolean removeAll(@NonNull Collection collection) { - return this.first.removeAll(collection) || this.second.removeAll(collection); + return this.first.removeAll(collection) | this.second.removeAll(collection); } @Override @@ -124,7 +121,7 @@ public void clear() { } private class CombinedIterator implements Iterator { - Iterator delegate = CombinedSet.this.first.iterator(); + Iterator delegate = CombinedCollection.this.first.iterator(); boolean iteratingFirst = true; @@ -135,7 +132,7 @@ public boolean hasNext() { return true; } else { if (this.iteratingFirst) { - this.delegate = CombinedSet.this.second.iterator(); + this.delegate = CombinedCollection.this.second.iterator(); this.iteratingFirst = false; return this.delegate.hasNext(); From dd4de63c8b3375e4f439dc7e0236435f30f6a581 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 14:36:36 -0800 Subject: [PATCH 03/14] make CombinedJarIndex final --- .../enigma/api/analysis/index/jar/CombinedJarIndex.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 0064007f6..185afd978 100644 --- 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 @@ -12,8 +12,8 @@ *

Note: currently the contained {@link ReferenceIndex} does not index main jar references to * library types and members. */ -public class CombinedJarIndex extends AbstractJarIndex { - public CombinedJarIndex( +public final class CombinedJarIndex extends AbstractJarIndex { + private CombinedJarIndex( EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { From 0b475dbe15314ccd729f9dc84bc4c8e9df96bf40 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 14:52:17 -0800 Subject: [PATCH 04/14] weaken Main/LibrariesJarIndex index field types and make them non-final with public constructors again --- .../api/analysis/index/jar/LibrariesJarIndex.java | 10 +++++----- .../enigma/api/analysis/index/jar/MainJarIndex.java | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) 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 12b39218e..8fc161045 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 @@ -9,12 +9,12 @@ /** * An index of the library jars of an {@link EnigmaProject}. */ -public final class LibrariesJarIndex extends AbstractJarIndex { - final EntryIndexImpl entryIndex; +public class LibrariesJarIndex extends AbstractJarIndex { + final EntryIndex entryIndex; final ReferenceIndex referenceIndex; - private LibrariesJarIndex( - EntryIndexImpl entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + public LibrariesJarIndex( + EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); @@ -27,7 +27,7 @@ private LibrariesJarIndex( * @return the newly created index */ public static LibrariesJarIndex empty() { - EntryIndexImpl entryIndex = new EntryIndexImpl(); + EntryIndex entryIndex = new EntryIndexImpl(); ReferenceIndex referenceIndex = new ReferenceIndexImpl(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); return new LibrariesJarIndex( 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 bf3068801..2befbed3f 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 @@ -9,12 +9,12 @@ /** * An index of the main jar of an {@link EnigmaProject}. */ -public final class MainJarIndex extends AbstractJarIndex { - final EntryIndexImpl entryIndex; +public class MainJarIndex extends AbstractJarIndex { + final EntryIndex entryIndex; final ReferenceIndex referenceIndex; - private MainJarIndex( - EntryIndexImpl entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + public MainJarIndex( + EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); @@ -27,7 +27,7 @@ private MainJarIndex( * @return the newly created index */ public static MainJarIndex empty() { - EntryIndexImpl entryIndex = new EntryIndexImpl(); + EntryIndex entryIndex = new EntryIndexImpl(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); ReferenceIndex referenceIndex = new ReferenceIndexImpl(); BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex); From 14b8307ab948f27332bbcd436a162f35d4677bd4 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 15:11:39 -0800 Subject: [PATCH 05/14] restore running custom indexers over CombinedJarIndex's scope --- .../java/org/quiltmc/enigma/api/Enigma.java | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) 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 4c372d655..ae1d3c8b2 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java @@ -6,7 +6,6 @@ import org.jspecify.annotations.Nullable; import org.objectweb.asm.Opcodes; import org.quiltmc.enigma.api.analysis.index.jar.CombinedJarIndex; -import org.quiltmc.enigma.api.analysis.index.jar.JarIndex; 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.mapping.MappingsIndex; @@ -31,6 +30,7 @@ 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.util.Either; import org.quiltmc.enigma.util.I18n; import org.quiltmc.enigma.util.Utils; @@ -46,7 +46,6 @@ 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; @@ -107,7 +106,7 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog JarClassProvider jarClassProvider = new JarClassProvider(path); MainJarIndex jarIndex = MainJarIndex.empty(); LibrariesJarIndex libIndex = LibrariesJarIndex.empty(); - JarIndex comboIndex = CombinedJarIndex.empty(jarIndex, libIndex); + CombinedJarIndex comboIndex = CombinedJarIndex.empty(jarIndex, libIndex); ClassLoaderClassProvider jreProvider = new ClassLoaderClassProvider(DriverManager.class.getClassLoader()); ClasspathClassProvider javaClassProvider = new ClasspathClassProvider(); @@ -116,13 +115,13 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog ProjectClassProvider projectClassProvider = new ProjectClassProvider(mainProjectProvider, librariesProvider); // main index - this.index(jarIndex, projectClassProvider, progress, "jar", false, jarIndex.getIndexableClassNames(projectClassProvider)); + this.index(jarIndex, projectClassProvider, progress, "jar", false); // lib index - this.index(libIndex, projectClassProvider, progress, "jar", true, libIndex.getIndexableClassNames(projectClassProvider)); + this.index(libIndex, projectClassProvider, progress, "jar", true); // combined main and lib index - this.index(comboIndex, projectClassProvider, progress, "combined", true, Collections.emptySet()); + this.index(comboIndex, projectClassProvider, progress, "combined", true); // name proposal var nameProposalServices = this.getNameProposalServices(); @@ -152,26 +151,24 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog } private void index( - JarIndex index, ProjectClassProvider classProvider, ProgressListener progress, String progressKey, - boolean includesLibraries, Collection customIndexerScope + AbstractJarIndex index, ProjectClassProvider classProvider, ProgressListener progress, String progressKey, + boolean includesLibraries ) { index.indexJar(classProvider, progress); - if (!customIndexerScope.isEmpty()) { - List indexers = this.services.get(JarIndexerService.TYPE); - progress.init(indexers.size(), I18n.translate("progress." + progressKey + ".custom_indexing")); + 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 (!(includesLibraries && !service.shouldIndexLibraries())) { - progress.step(i++, I18n.translateFormatted("progress." + progressKey + ".custom_indexing.indexer", service.getId())); - Set scope = new HashSet<>(customIndexerScope); - service.acceptJar(scope, classProvider, index); - } + int i = 1; + for (var service : indexers) { + if (!(includesLibraries && !service.shouldIndexLibraries())) { + progress.step(i++, I18n.translateFormatted("progress." + progressKey + ".custom_indexing.indexer", service.getId())); + Set scope = new HashSet<>(index.getIndexableClassNames(classProvider)); + service.acceptJar(scope, classProvider, index); } - - progress.step(i, I18n.translate("progress." + progressKey + ".custom_indexing.finished")); } + + progress.step(i, I18n.translate("progress." + progressKey + ".custom_indexing.finished")); } public EnigmaProfile getProfile() { From 1baa29366a1c079415c5ea4453fc6e3b62494310 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 16:49:31 -0800 Subject: [PATCH 06/14] make CombinedJarIndex's BridgeMethodIndex a combination of MainJarIndex's and LibrariesJarIndex's --- .../analysis/index/jar/BridgeMethodIndex.java | 179 ++---------------- .../index/jar/BridgeMethodIndexImpl.java | 179 ++++++++++++++++++ .../index/jar/CombinedBridgeMethodIndex.java | 66 +++++++ .../analysis/index/jar/CombinedJarIndex.java | 2 +- .../analysis/index/jar/LibrariesJarIndex.java | 4 +- .../api/analysis/index/jar/MainJarIndex.java | 4 +- .../org/quiltmc/enigma/util/CombinedSet.java | 9 + .../enigma/util/UnmodifiableCombinedMap.java | 103 ++++++++++ 8 files changed, 383 insertions(+), 163 deletions(-) create mode 100644 enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndexImpl.java create mode 100644 enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java create mode 100644 enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java create mode 100644 enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java index 146c66522..0f853b570 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java @@ -1,180 +1,39 @@ package org.quiltmc.enigma.api.analysis.index.jar; -import com.google.common.collect.Maps; import org.jspecify.annotations.Nullable; -import org.quiltmc.enigma.api.translation.representation.AccessFlags; -import org.quiltmc.enigma.api.translation.representation.ArgumentDescriptor; -import org.quiltmc.enigma.api.translation.representation.MethodDescriptor; -import org.quiltmc.enigma.api.translation.representation.TypeDescriptor; -import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; -import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; import java.util.Map; -public class BridgeMethodIndex implements JarIndexer { - private final EntryIndex entryIndex; - private final InheritanceIndex inheritanceIndex; - private final ReferenceIndex referenceIndex; +public interface BridgeMethodIndex extends JarIndexer { + void findBridgeMethods(); - private final Map bridgeToSpecialized = Maps.newHashMap(); - private final Map specializedToBridge = Maps.newHashMap(); + boolean isBridgeMethod(MethodEntry entry); - public BridgeMethodIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) { - this.entryIndex = entryIndex; - this.inheritanceIndex = inheritanceIndex; - this.referenceIndex = referenceIndex; - } - - public void findBridgeMethods() { - // look for access and bridged methods - for (MethodEntry methodEntry : this.entryIndex.getMethods()) { - MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry; - - AccessFlags access = methodDefEntry.getAccess(); - if (access == null || !access.isSynthetic()) { - continue; - } - - this.indexSyntheticMethod(methodDefEntry, access); - } - } - - @Override - public void processIndex(JarIndex index) { - Map copiedAccessToBridge = new HashMap<>(this.specializedToBridge); - - for (Map.Entry entry : copiedAccessToBridge.entrySet()) { - MethodEntry specializedEntry = entry.getKey(); - MethodEntry bridgeEntry = entry.getValue(); - if (bridgeEntry.getName().equals(specializedEntry.getName())) { - continue; - } - - MethodEntry renamedSpecializedEntry = specializedEntry.withName(bridgeEntry.getName()); - this.specializedToBridge.put(renamedSpecializedEntry, this.specializedToBridge.get(specializedEntry)); - } - } - - private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) { - MethodEntry specializedMethod = this.findSpecializedMethod(syntheticMethod); - if (specializedMethod == null) { - return; - } - - if (access.isBridge() || this.isPotentialBridge(syntheticMethod, specializedMethod)) { - this.bridgeToSpecialized.put(syntheticMethod, specializedMethod); - if (this.specializedToBridge.containsKey(specializedMethod)) { - // we already have a bridge for this method, so we keep the one higher in the hierarchy - // can happen with a class inheriting from a superclass with one or more bridge method(s) - MethodEntry bridgeMethod = this.specializedToBridge.get(specializedMethod); - this.specializedToBridge.put(specializedMethod, this.getHigherMethod(syntheticMethod, bridgeMethod)); - } else { - this.specializedToBridge.put(specializedMethod, syntheticMethod); - } - } - } - - private MethodEntry findSpecializedMethod(MethodEntry method) { - // we want to find all compiler-added methods that directly call another with no processing - - // get all the methods that we call - final Collection referencedMethods = this.referenceIndex.getMethodsReferencedBy(method); - - // is there just one? - if (referencedMethods.size() != 1) { - return null; - } - - return referencedMethods.stream().findFirst().orElse(null); - } - - private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry specializedMethod) { - // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited - AccessFlags bridgeAccess = bridgeMethod.getAccess(); - if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) { - return false; - } - - MethodDescriptor bridgeDesc = bridgeMethod.getDesc(); - MethodDescriptor specializedDesc = specializedMethod.getDesc(); - List bridgeArguments = bridgeDesc.getArgumentDescs(); - List specializedArguments = specializedDesc.getArgumentDescs(); - - // A bridge method will always have the same number of arguments - if (bridgeArguments.size() != specializedArguments.size()) { - return false; - } - - // Check that all argument types are bridge-compatible - for (int i = 0; i < bridgeArguments.size(); i++) { - if (!this.areTypesBridgeCompatible(bridgeArguments.get(i), specializedArguments.get(i))) { - return false; - } - } - - // Check that the return type is bridge-compatible - return this.areTypesBridgeCompatible(bridgeDesc.getReturnDesc(), specializedDesc.getReturnDesc()); - } - - private boolean areTypesBridgeCompatible(TypeDescriptor bridgeDesc, TypeDescriptor specializedDesc) { - if (bridgeDesc.equals(specializedDesc)) { - return true; - } - - // Either the descs will be equal, or they are both types and different through a generic - if (bridgeDesc.isType() && specializedDesc.isType()) { - ClassEntry bridgeType = bridgeDesc.getTypeEntry(); - ClassEntry accessedType = specializedDesc.getTypeEntry(); - - // If the given types are completely unrelated to each other, this can't be bridge compatible - InheritanceIndex.Relation relation = this.inheritanceIndex.computeClassRelation(accessedType, bridgeType); - return relation != InheritanceIndex.Relation.UNRELATED; - } - - return false; - } - - // Get the method higher in the hierarchy - private MethodEntry getHigherMethod(MethodEntry bridgeMethod1, MethodEntry bridgeMethod2) { - ClassEntry parent1 = bridgeMethod1.getParent(); - ClassEntry parent2 = bridgeMethod2.getParent(); - return this.inheritanceIndex.getDescendants(parent1).contains(parent2) ? bridgeMethod1 : bridgeMethod2; - } - - public boolean isBridgeMethod(MethodEntry entry) { - return this.bridgeToSpecialized.containsKey(entry); - } - - public boolean isSpecializedMethod(MethodEntry entry) { - return this.specializedToBridge.containsKey(entry); - } + boolean isSpecializedMethod(MethodEntry entry); @Nullable - public MethodEntry getBridgeFromSpecialized(MethodEntry specialized) { - return this.specializedToBridge.get(specialized); - } + MethodEntry getBridgeFromSpecialized(MethodEntry specialized); - public MethodEntry getSpecializedFromBridge(MethodEntry bridge) { - return this.bridgeToSpecialized.get(bridge); - } + MethodEntry getSpecializedFromBridge(MethodEntry bridge); - /** Includes "renamed specialized -> bridge" entries. */ - public Map getSpecializedToBridge() { - return Collections.unmodifiableMap(this.specializedToBridge); - } + /** + * Includes "renamed specialized -> bridge" entries. + */ + Map getSpecializedToBridge(); - /** Only "bridge -> original name" entries. **/ - public Map getBridgeToSpecialized() { - return Collections.unmodifiableMap(this.bridgeToSpecialized); + /** + * Only "bridge -> original name" entries. + */ + Map getBridgeToSpecialized(); + + @Override + default Class getType() { + return BridgeMethodIndex.class; } @Override - public String getTranslationKey() { + default String getTranslationKey() { return "progress.jar.indexing.process.bridge_methods"; } } diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndexImpl.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndexImpl.java new file mode 100644 index 000000000..4c9f5184b --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndexImpl.java @@ -0,0 +1,179 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import com.google.common.collect.Maps; +import org.jspecify.annotations.Nullable; +import org.quiltmc.enigma.api.translation.representation.AccessFlags; +import org.quiltmc.enigma.api.translation.representation.ArgumentDescriptor; +import org.quiltmc.enigma.api.translation.representation.MethodDescriptor; +import org.quiltmc.enigma.api.translation.representation.TypeDescriptor; +import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodDefEntry; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class BridgeMethodIndexImpl implements BridgeMethodIndex { + private final EntryIndex entryIndex; + private final InheritanceIndex inheritanceIndex; + private final ReferenceIndex referenceIndex; + + private final Map bridgeToSpecialized = Maps.newHashMap(); + private final Map specializedToBridge = Maps.newHashMap(); + + BridgeMethodIndexImpl(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) { + this.entryIndex = entryIndex; + this.inheritanceIndex = inheritanceIndex; + this.referenceIndex = referenceIndex; + } + + @Override + public void findBridgeMethods() { + // look for access and bridged methods + for (MethodEntry methodEntry : this.entryIndex.getMethods()) { + MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry; + + AccessFlags access = methodDefEntry.getAccess(); + if (access == null || !access.isSynthetic()) { + continue; + } + + this.indexSyntheticMethod(methodDefEntry, access); + } + } + + @Override + public void processIndex(JarIndex index) { + Map copiedAccessToBridge = new HashMap<>(this.specializedToBridge); + + for (Map.Entry entry : copiedAccessToBridge.entrySet()) { + MethodEntry specializedEntry = entry.getKey(); + MethodEntry bridgeEntry = entry.getValue(); + if (bridgeEntry.getName().equals(specializedEntry.getName())) { + continue; + } + + MethodEntry renamedSpecializedEntry = specializedEntry.withName(bridgeEntry.getName()); + this.specializedToBridge.put(renamedSpecializedEntry, this.specializedToBridge.get(specializedEntry)); + } + } + + private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) { + MethodEntry specializedMethod = this.findSpecializedMethod(syntheticMethod); + if (specializedMethod == null) { + return; + } + + if (access.isBridge() || this.isPotentialBridge(syntheticMethod, specializedMethod)) { + this.bridgeToSpecialized.put(syntheticMethod, specializedMethod); + if (this.specializedToBridge.containsKey(specializedMethod)) { + // we already have a bridge for this method, so we keep the one higher in the hierarchy + // can happen with a class inheriting from a superclass with one or more bridge method(s) + MethodEntry bridgeMethod = this.specializedToBridge.get(specializedMethod); + this.specializedToBridge.put(specializedMethod, this.getHigherMethod(syntheticMethod, bridgeMethod)); + } else { + this.specializedToBridge.put(specializedMethod, syntheticMethod); + } + } + } + + private MethodEntry findSpecializedMethod(MethodEntry method) { + // we want to find all compiler-added methods that directly call another with no processing + + // get all the methods that we call + final Collection referencedMethods = this.referenceIndex.getMethodsReferencedBy(method); + + // is there just one? + if (referencedMethods.size() != 1) { + return null; + } + + return referencedMethods.stream().findFirst().orElse(null); + } + + private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry specializedMethod) { + // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited + AccessFlags bridgeAccess = bridgeMethod.getAccess(); + if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) { + return false; + } + + MethodDescriptor bridgeDesc = bridgeMethod.getDesc(); + MethodDescriptor specializedDesc = specializedMethod.getDesc(); + List bridgeArguments = bridgeDesc.getArgumentDescs(); + List specializedArguments = specializedDesc.getArgumentDescs(); + + // A bridge method will always have the same number of arguments + if (bridgeArguments.size() != specializedArguments.size()) { + return false; + } + + // Check that all argument types are bridge-compatible + for (int i = 0; i < bridgeArguments.size(); i++) { + if (!this.areTypesBridgeCompatible(bridgeArguments.get(i), specializedArguments.get(i))) { + return false; + } + } + + // Check that the return type is bridge-compatible + return this.areTypesBridgeCompatible(bridgeDesc.getReturnDesc(), specializedDesc.getReturnDesc()); + } + + private boolean areTypesBridgeCompatible(TypeDescriptor bridgeDesc, TypeDescriptor specializedDesc) { + if (bridgeDesc.equals(specializedDesc)) { + return true; + } + + // Either the descs will be equal, or they are both types and different through a generic + if (bridgeDesc.isType() && specializedDesc.isType()) { + ClassEntry bridgeType = bridgeDesc.getTypeEntry(); + ClassEntry accessedType = specializedDesc.getTypeEntry(); + + // If the given types are completely unrelated to each other, this can't be bridge compatible + InheritanceIndex.Relation relation = this.inheritanceIndex.computeClassRelation(accessedType, bridgeType); + return relation != InheritanceIndex.Relation.UNRELATED; + } + + return false; + } + + // Get the method higher in the hierarchy + private MethodEntry getHigherMethod(MethodEntry bridgeMethod1, MethodEntry bridgeMethod2) { + ClassEntry parent1 = bridgeMethod1.getParent(); + ClassEntry parent2 = bridgeMethod2.getParent(); + return this.inheritanceIndex.getDescendants(parent1).contains(parent2) ? bridgeMethod1 : bridgeMethod2; + } + + @Override + public boolean isBridgeMethod(MethodEntry entry) { + return this.bridgeToSpecialized.containsKey(entry); + } + + @Override + public boolean isSpecializedMethod(MethodEntry entry) { + return this.specializedToBridge.containsKey(entry); + } + + @Override + public @Nullable MethodEntry getBridgeFromSpecialized(MethodEntry specialized) { + return this.specializedToBridge.get(specialized); + } + + @Override + public MethodEntry getSpecializedFromBridge(MethodEntry bridge) { + return this.bridgeToSpecialized.get(bridge); + } + + @Override + public Map getSpecializedToBridge() { + return Collections.unmodifiableMap(this.specializedToBridge); + } + + @Override + public Map getBridgeToSpecialized() { + return Collections.unmodifiableMap(this.bridgeToSpecialized); + } +} diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java new file mode 100644 index 000000000..5c57b6c5c --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java @@ -0,0 +1,66 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import org.jspecify.annotations.Nullable; +import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry; +import org.quiltmc.enigma.util.UnmodifiableCombinedMap; + +import java.util.Map; + +class CombinedBridgeMethodIndex implements BridgeMethodIndex { + private final BridgeMethodIndex mainIndex; + private final BridgeMethodIndex libIndex; + private final Map specializedToBridge; + private final Map bridgeToSpecialized; + + CombinedBridgeMethodIndex(BridgeMethodIndex mainIndex, BridgeMethodIndex libIndex) { + this.mainIndex = mainIndex; + this.libIndex = libIndex; + + this.specializedToBridge = new UnmodifiableCombinedMap<>( + this.mainIndex.getSpecializedToBridge(), + this.libIndex.getSpecializedToBridge() + ); + + this.bridgeToSpecialized = new UnmodifiableCombinedMap<>( + this.mainIndex.getBridgeToSpecialized(), + this.libIndex.getBridgeToSpecialized() + ); + } + + @Override + public void findBridgeMethods() { + // this is done by the main and lib delegates + } + + @Override + public boolean isBridgeMethod(MethodEntry entry) { + return this.mainIndex.isBridgeMethod(entry) || this.libIndex.isBridgeMethod(entry); + } + + @Override + public boolean isSpecializedMethod(MethodEntry entry) { + return this.mainIndex.isSpecializedMethod(entry) || this.libIndex.isSpecializedMethod(entry); + } + + @Override + public @Nullable MethodEntry getBridgeFromSpecialized(MethodEntry specialized) { + final MethodEntry mainBridge = this.mainIndex.getBridgeFromSpecialized(specialized); + return mainBridge == null ? this.libIndex.getBridgeFromSpecialized(specialized) : mainBridge; + } + + @Override + public MethodEntry getSpecializedFromBridge(MethodEntry bridge) { + final MethodEntry mainSpecialized = this.mainIndex.getSpecializedFromBridge(bridge); + return mainSpecialized == null ? this.libIndex.getSpecializedFromBridge(bridge) : mainSpecialized; + } + + @Override + public Map getSpecializedToBridge() { + return this.specializedToBridge; + } + + @Override + public Map getBridgeToSpecialized() { + return this.bridgeToSpecialized; + } +} 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 index 185afd978..2bfb3db93 100644 --- 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 @@ -31,7 +31,7 @@ public static CombinedJarIndex empty(MainJarIndex mainIndex, LibrariesJarIndex l LambdaIndex lambdaIndex = new LambdaIndex(); return new CombinedJarIndex( entryIndex, inheritanceIndex, referenceIndex, - new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex), + new CombinedBridgeMethodIndex(mainIndex.bridgeMethodIndex, libIndex.bridgeMethodIndex), // required by MappingValidator lambdaIndex ); 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 8fc161045..18ddb1e28 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 @@ -12,6 +12,7 @@ public class LibrariesJarIndex extends AbstractJarIndex { final EntryIndex entryIndex; final ReferenceIndex referenceIndex; + final BridgeMethodIndex bridgeMethodIndex; public LibrariesJarIndex( EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, @@ -20,6 +21,7 @@ public LibrariesJarIndex( super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); this.entryIndex = entryIndex; this.referenceIndex = referenceIndex; + this.bridgeMethodIndex = bridgeMethodIndex; } /** @@ -32,7 +34,7 @@ public static LibrariesJarIndex empty() { InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); return new LibrariesJarIndex( entryIndex, inheritanceIndex, referenceIndex, - new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex) + new BridgeMethodIndexImpl(entryIndex, inheritanceIndex, referenceIndex) ); } 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 2befbed3f..066c14aa8 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 @@ -12,6 +12,7 @@ public class MainJarIndex extends AbstractJarIndex { final EntryIndex entryIndex; final ReferenceIndex referenceIndex; + final BridgeMethodIndex bridgeMethodIndex; public MainJarIndex( EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, @@ -20,6 +21,7 @@ public MainJarIndex( super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); this.entryIndex = entryIndex; this.referenceIndex = referenceIndex; + this.bridgeMethodIndex = bridgeMethodIndex; } /** @@ -30,7 +32,7 @@ public static MainJarIndex empty() { EntryIndex entryIndex = new EntryIndexImpl(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); ReferenceIndex referenceIndex = new ReferenceIndexImpl(); - BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex); + BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndexImpl(entryIndex, inheritanceIndex, referenceIndex); PackageVisibilityIndex packageVisibilityIndex = new PackageVisibilityIndex(); EnclosingMethodIndex enclosingMethodIndex = new EnclosingMethodIndex(); LambdaIndex lambdaIndex = new LambdaIndex(); diff --git a/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java new file mode 100644 index 000000000..7e2cd6a4b --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedSet.java @@ -0,0 +1,9 @@ +package org.quiltmc.enigma.util; + +import java.util.Set; + +public class CombinedSet extends CombinedCollection implements Set { + public CombinedSet(Set first, Set second) { + super(first, second); + } +} diff --git a/enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java b/enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java new file mode 100644 index 000000000..c2a47268a --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java @@ -0,0 +1,103 @@ +package org.quiltmc.enigma.util; + +import org.jspecify.annotations.NonNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * An unmodifiable view of two backing maps. + * + *

Only intended for maps no {@link #keySet()} overlap. Behavior is undefined in the case of overlap. + * + * @param the type of keys + * @param the type of values + */ +public class UnmodifiableCombinedMap implements Map { + private static UnsupportedOperationException createUnsupportedModificationException() { + return new UnsupportedOperationException("cannot modify combined map!"); + } + + private final Map first; + private final Map second; + + private final Set keySet; + private final Collection values; + private final Set> entrySet; + + public UnmodifiableCombinedMap(Map first, Map second) { + this.first = first; + this.second = second; + + this.keySet = Collections.unmodifiableSet(new CombinedSet<>(this.first.keySet(), this.second.keySet())); + this.values = Collections + .unmodifiableCollection(new CombinedCollection<>(this.first.values(), this.second.values())); + this.entrySet = Collections.unmodifiableSet(new CombinedSet<>(this.first.entrySet(), this.second.entrySet())); + } + + @Override + public int size() { + return this.first.size() + this.second.size(); + } + + @Override + public boolean isEmpty() { + return this.first.isEmpty() && this.second.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return this.first.containsKey(key) || this.second.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return this.first.containsValue(value) || this.second.containsValue(value); + } + + @SuppressWarnings("SuspiciousMethodCalls") + @Override + public V get(Object key) { + return this.first.containsKey(key) ? this.first.get(key) : this.second.get(key); + } + + @Override + public V put(K key, V value) { + throw createUnsupportedModificationException(); + } + + @Override + public V remove(Object key) { + throw createUnsupportedModificationException(); + } + + @Override + public void putAll(@NonNull Map m) { + throw createUnsupportedModificationException(); + } + + @Override + public void clear() { + throw createUnsupportedModificationException(); + } + + @NonNull + @Override + public Set keySet() { + return this.keySet; + } + + @NonNull + @Override + public Collection values() { + return this.values; + } + + @NonNull + @Override + public Set> entrySet() { + return this.entrySet; + } +} From 68ef32cbc3d7af585e1f136426d6bd77ea33d344 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 17:38:23 -0800 Subject: [PATCH 07/14] extract IndependentJarIndex superclass of Main/LibraryJarIndex --- .../analysis/index/jar/CombinedJarIndex.java | 7 ++-- .../index/jar/IndependentJarIndex.java | 32 +++++++++++++++++++ .../analysis/index/jar/LibrariesJarIndex.java | 10 +----- .../api/analysis/index/jar/MainJarIndex.java | 10 +----- 4 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentJarIndex.java 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 index 2bfb3db93..9de97c2d1 100644 --- 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 @@ -25,13 +25,14 @@ private CombinedJarIndex( * @return the newly created index */ public static CombinedJarIndex empty(MainJarIndex mainIndex, LibrariesJarIndex libIndex) { - EntryIndex entryIndex = new CombinedEntryIndex(mainIndex.entryIndex, libIndex.entryIndex); - ReferenceIndex referenceIndex = new CombinedReferenceIndex(mainIndex.referenceIndex, libIndex.referenceIndex); + EntryIndex entryIndex = new CombinedEntryIndex(mainIndex.getEntryIndex(), libIndex.getEntryIndex()); + ReferenceIndex referenceIndex = + new CombinedReferenceIndex(mainIndex.getReferenceIndex(), libIndex.getReferenceIndex()); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); LambdaIndex lambdaIndex = new LambdaIndex(); return new CombinedJarIndex( entryIndex, inheritanceIndex, referenceIndex, - new CombinedBridgeMethodIndex(mainIndex.bridgeMethodIndex, libIndex.bridgeMethodIndex), + new CombinedBridgeMethodIndex(mainIndex.getBridgeMethodIndex(), libIndex.getBridgeMethodIndex()), // required by MappingValidator lambdaIndex ); diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentJarIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentJarIndex.java new file mode 100644 index 000000000..e3661d552 --- /dev/null +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentJarIndex.java @@ -0,0 +1,32 @@ +package org.quiltmc.enigma.api.analysis.index.jar; + +import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex; + +abstract class IndependentJarIndex extends AbstractJarIndex { + private final EntryIndex entryIndex; + private final ReferenceIndex referenceIndex; + private final BridgeMethodIndex bridgeMethodIndex; + + IndependentJarIndex( + EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, + BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers + ) { + super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); + + this.entryIndex = entryIndex; + this.referenceIndex = referenceIndex; + this.bridgeMethodIndex = bridgeMethodIndex; + } + + EntryIndex getEntryIndex() { + return this.entryIndex; + } + + ReferenceIndex getReferenceIndex() { + return this.referenceIndex; + } + + BridgeMethodIndex getBridgeMethodIndex() { + return this.bridgeMethodIndex; + } +} 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 18ddb1e28..0004cecfe 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 @@ -2,26 +2,18 @@ import org.quiltmc.enigma.api.EnigmaProject; import org.quiltmc.enigma.api.class_provider.ProjectClassProvider; -import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex; import java.util.Collection; /** * An index of the library jars of an {@link EnigmaProject}. */ -public class LibrariesJarIndex extends AbstractJarIndex { - final EntryIndex entryIndex; - final ReferenceIndex referenceIndex; - final BridgeMethodIndex bridgeMethodIndex; - +public class LibrariesJarIndex extends IndependentJarIndex { public LibrariesJarIndex( EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); - this.entryIndex = entryIndex; - this.referenceIndex = referenceIndex; - this.bridgeMethodIndex = bridgeMethodIndex; } /** 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 066c14aa8..f3fadf27e 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 @@ -2,26 +2,18 @@ import org.quiltmc.enigma.api.EnigmaProject; import org.quiltmc.enigma.api.class_provider.ProjectClassProvider; -import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex; import java.util.Collection; /** * An index of the main jar of an {@link EnigmaProject}. */ -public class MainJarIndex extends AbstractJarIndex { - final EntryIndex entryIndex; - final ReferenceIndex referenceIndex; - final BridgeMethodIndex bridgeMethodIndex; - +public class MainJarIndex extends IndependentJarIndex { public MainJarIndex( EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, JarIndexer... otherIndexers ) { super(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, otherIndexers); - this.entryIndex = entryIndex; - this.referenceIndex = referenceIndex; - this.bridgeMethodIndex = bridgeMethodIndex; } /** From 44c5e438da33fcefb8392dfc3b6d2ae3d8eba80e Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Wed, 3 Dec 2025 17:43:15 -0800 Subject: [PATCH 08/14] replace index Impl suffixes with Indepenedent prefixes --- ...thodIndexImpl.java => IndependentBridgeMethodIndex.java} | 4 ++-- .../jar/{EntryIndexImpl.java => IndependentEntryIndex.java} | 2 +- ...ferenceIndexImpl.java => IndependentReferenceIndex.java} | 2 +- .../enigma/api/analysis/index/jar/LibrariesJarIndex.java | 6 +++--- .../quiltmc/enigma/api/analysis/index/jar/MainJarIndex.java | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) rename enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/{BridgeMethodIndexImpl.java => IndependentBridgeMethodIndex.java} (97%) rename enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/{EntryIndexImpl.java => IndependentEntryIndex.java} (99%) rename enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/{ReferenceIndexImpl.java => IndependentReferenceIndex.java} (99%) diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndexImpl.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentBridgeMethodIndex.java similarity index 97% rename from enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndexImpl.java rename to enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentBridgeMethodIndex.java index 4c9f5184b..8843a46bd 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndexImpl.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentBridgeMethodIndex.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.Map; -class BridgeMethodIndexImpl implements BridgeMethodIndex { +class IndependentBridgeMethodIndex implements BridgeMethodIndex { private final EntryIndex entryIndex; private final InheritanceIndex inheritanceIndex; private final ReferenceIndex referenceIndex; @@ -24,7 +24,7 @@ class BridgeMethodIndexImpl implements BridgeMethodIndex { private final Map bridgeToSpecialized = Maps.newHashMap(); private final Map specializedToBridge = Maps.newHashMap(); - BridgeMethodIndexImpl(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) { + IndependentBridgeMethodIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) { this.entryIndex = entryIndex; this.inheritanceIndex = inheritanceIndex; this.referenceIndex = referenceIndex; diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentEntryIndex.java similarity index 99% rename from enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java rename to enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentEntryIndex.java index af624360b..0200e3955 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndexImpl.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentEntryIndex.java @@ -19,7 +19,7 @@ import java.util.HashMap; import java.util.Map; -class EntryIndexImpl implements EntryIndex { +class IndependentEntryIndex implements EntryIndex { private final EntryTree tree = new HashEntryTree<>(); private final Map fieldDefinitions = new HashMap<>(); diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndexImpl.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentReferenceIndex.java similarity index 99% rename from enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndexImpl.java rename to enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentReferenceIndex.java index 32147fa12..c89cac257 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/ReferenceIndexImpl.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentReferenceIndex.java @@ -18,7 +18,7 @@ import java.util.Collection; import java.util.Map; -class ReferenceIndexImpl implements ReferenceIndex { +class IndependentReferenceIndex implements ReferenceIndex { private Multimap methodReferences = HashMultimap.create(); private Multimap fieldReferences = HashMultimap.create(); 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 0004cecfe..f3a2a4fda 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 @@ -21,12 +21,12 @@ public LibrariesJarIndex( * @return the newly created index */ public static LibrariesJarIndex empty() { - EntryIndex entryIndex = new EntryIndexImpl(); - ReferenceIndex referenceIndex = new ReferenceIndexImpl(); + EntryIndex entryIndex = new IndependentEntryIndex(); + ReferenceIndex referenceIndex = new IndependentReferenceIndex(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); return new LibrariesJarIndex( entryIndex, inheritanceIndex, referenceIndex, - new BridgeMethodIndexImpl(entryIndex, inheritanceIndex, referenceIndex) + new IndependentBridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex) ); } 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 f3fadf27e..95fb08eef 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 @@ -21,10 +21,10 @@ public MainJarIndex( * @return the newly created index */ public static MainJarIndex empty() { - EntryIndex entryIndex = new EntryIndexImpl(); + EntryIndex entryIndex = new IndependentEntryIndex(); InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex); - ReferenceIndex referenceIndex = new ReferenceIndexImpl(); - BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndexImpl(entryIndex, inheritanceIndex, referenceIndex); + ReferenceIndex referenceIndex = new IndependentReferenceIndex(); + BridgeMethodIndex bridgeMethodIndex = new IndependentBridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex); PackageVisibilityIndex packageVisibilityIndex = new PackageVisibilityIndex(); EnclosingMethodIndex enclosingMethodIndex = new EnclosingMethodIndex(); LambdaIndex lambdaIndex = new LambdaIndex(); From 90978ff844d76624ad1924c330461e45b1e51dc1 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Fri, 5 Dec 2025 11:48:48 -0800 Subject: [PATCH 09/14] seal new index interfaces --- .../enigma/api/analysis/index/jar/BridgeMethodIndex.java | 3 ++- .../api/analysis/index/jar/CombinedBridgeMethodIndex.java | 2 +- .../enigma/api/analysis/index/jar/CombinedEntryIndex.java | 2 +- .../enigma/api/analysis/index/jar/CombinedReferenceIndex.java | 2 +- .../org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java | 2 +- .../api/analysis/index/jar/IndependentBridgeMethodIndex.java | 2 +- .../enigma/api/analysis/index/jar/IndependentEntryIndex.java | 2 +- .../api/analysis/index/jar/IndependentReferenceIndex.java | 2 +- .../quiltmc/enigma/api/analysis/index/jar/ReferenceIndex.java | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java index 0f853b570..4422fbe86 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/BridgeMethodIndex.java @@ -5,7 +5,8 @@ import java.util.Map; -public interface BridgeMethodIndex extends JarIndexer { +public sealed interface BridgeMethodIndex extends JarIndexer + permits CombinedBridgeMethodIndex, IndependentBridgeMethodIndex { void findBridgeMethods(); boolean isBridgeMethod(MethodEntry entry); diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java index 5c57b6c5c..7ffac9680 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedBridgeMethodIndex.java @@ -6,7 +6,7 @@ import java.util.Map; -class CombinedBridgeMethodIndex implements BridgeMethodIndex { +final class CombinedBridgeMethodIndex implements BridgeMethodIndex { private final BridgeMethodIndex mainIndex; private final BridgeMethodIndex libIndex; private final Map specializedToBridge; diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java index edd874be2..f15340abc 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java @@ -17,7 +17,7 @@ import java.util.Collection; -class CombinedEntryIndex implements EntryIndex { +final class CombinedEntryIndex implements EntryIndex { private final EntryIndex mainIndex; private final EntryIndex libIndex; diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java index d63dba2f6..db780c980 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedReferenceIndex.java @@ -13,7 +13,7 @@ /** * Note: does not currently index main jar references to library types and members. */ -class CombinedReferenceIndex implements ReferenceIndex { +final class CombinedReferenceIndex implements ReferenceIndex { private final ReferenceIndex mainIndex; private final ReferenceIndex libIndex; diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java index 385e725f8..545b41211 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/EntryIndex.java @@ -17,7 +17,7 @@ import java.util.Collection; -public interface EntryIndex extends JarIndexer { +public sealed interface EntryIndex extends JarIndexer permits CombinedEntryIndex, IndependentEntryIndex { boolean hasClass(ClassEntry entry); boolean hasMethod(MethodEntry entry); diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentBridgeMethodIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentBridgeMethodIndex.java index 8843a46bd..61b26ed61 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentBridgeMethodIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentBridgeMethodIndex.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.Map; -class IndependentBridgeMethodIndex implements BridgeMethodIndex { +final class IndependentBridgeMethodIndex implements BridgeMethodIndex { private final EntryIndex entryIndex; private final InheritanceIndex inheritanceIndex; private final ReferenceIndex referenceIndex; diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentEntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentEntryIndex.java index 0200e3955..2f0079434 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentEntryIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentEntryIndex.java @@ -19,7 +19,7 @@ import java.util.HashMap; import java.util.Map; -class IndependentEntryIndex implements EntryIndex { +final class IndependentEntryIndex implements EntryIndex { private final EntryTree tree = new HashEntryTree<>(); private final Map fieldDefinitions = new HashMap<>(); diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentReferenceIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentReferenceIndex.java index c89cac257..de27f630d 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentReferenceIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/IndependentReferenceIndex.java @@ -18,7 +18,7 @@ import java.util.Collection; import java.util.Map; -class IndependentReferenceIndex implements ReferenceIndex { +final class IndependentReferenceIndex implements ReferenceIndex { private Multimap methodReferences = HashMultimap.create(); private Multimap fieldReferences = HashMultimap.create(); 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 6575c26b2..fdbea1c8f 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 @@ -9,7 +9,7 @@ import java.util.Collection; -public interface ReferenceIndex extends JarIndexer { +public sealed interface ReferenceIndex extends JarIndexer permits CombinedReferenceIndex, IndependentReferenceIndex { Collection getMethodsReferencedBy(MethodEntry entry); Collection getFieldsReferencedBy(MethodEntry entry); From bab3164ceb2ed02f1bac018933606bd103865572 Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Fri, 12 Dec 2025 12:20:04 -0800 Subject: [PATCH 10/14] implement CombinedEntryIndex::getTree --- .../analysis/index/jar/CombinedEntryIndex.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java index f15340abc..b29a4ff4e 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java @@ -3,6 +3,7 @@ import org.jspecify.annotations.Nullable; import org.quiltmc.enigma.api.translation.mapping.EntryMapping; import org.quiltmc.enigma.api.translation.mapping.tree.EntryTree; +import org.quiltmc.enigma.api.translation.mapping.tree.MergedEntryMappingTree; import org.quiltmc.enigma.api.translation.representation.AccessFlags; import org.quiltmc.enigma.api.translation.representation.entry.ClassDefEntry; import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; @@ -26,6 +27,14 @@ final class CombinedEntryIndex implements EntryIndex { private final Collection parameters; private final Collection fields; + /** + * Lazily populated cache. + * + * @see #getTree() + */ + @Nullable + private EntryTree tree; + CombinedEntryIndex(EntryIndex mainIndex, EntryIndex libIndex) { this.mainIndex = mainIndex; this.libIndex = libIndex; @@ -142,7 +151,10 @@ public Collection getFields() { @Override public EntryTree getTree() { - // TODO - throw new UnsupportedOperationException(); + if (this.tree == null) { + this.tree = new MergedEntryMappingTree(this.mainIndex.getTree(), this.libIndex.getTree()); + } + + return this.tree; } } From acae8f4d464b1df06b01410a413b990724ed3beb Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Fri, 12 Dec 2025 12:30:19 -0800 Subject: [PATCH 11/14] fix passing 'jar' instead of 'libs' for translation key --- enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ae1d3c8b2..f44cf4f5c 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java @@ -118,7 +118,7 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog this.index(jarIndex, projectClassProvider, progress, "jar", false); // lib index - this.index(libIndex, projectClassProvider, progress, "jar", true); + this.index(libIndex, projectClassProvider, progress, "libs", true); // combined main and lib index this.index(comboIndex, projectClassProvider, progress, "combined", true); From 2fdf88ec618c226c63c133ed065e05416d14584e Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Fri, 12 Dec 2025 17:20:57 -0800 Subject: [PATCH 12/14] make tree not lazy (MergedEntryMappingTree has no state except for its components anyways) --- .../api/analysis/index/jar/CombinedEntryIndex.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java index b29a4ff4e..3a557bab6 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java +++ b/enigma/src/main/java/org/quiltmc/enigma/api/analysis/index/jar/CombinedEntryIndex.java @@ -27,13 +27,7 @@ final class CombinedEntryIndex implements EntryIndex { private final Collection parameters; private final Collection fields; - /** - * Lazily populated cache. - * - * @see #getTree() - */ - @Nullable - private EntryTree tree; + private final EntryTree tree; CombinedEntryIndex(EntryIndex mainIndex, EntryIndex libIndex) { this.mainIndex = mainIndex; @@ -43,6 +37,8 @@ final class CombinedEntryIndex implements EntryIndex { this.methods = new CombinedCollection<>(this.mainIndex.getMethods(), this.libIndex.getMethods()); this.parameters = new CombinedCollection<>(this.mainIndex.getParameters(), this.libIndex.getParameters()); this.fields = new CombinedCollection<>(this.mainIndex.getFields(), this.libIndex.getFields()); + + this.tree = new MergedEntryMappingTree(this.mainIndex.getTree(), this.libIndex.getTree()); } @Override @@ -151,10 +147,6 @@ public Collection getFields() { @Override public EntryTree getTree() { - if (this.tree == null) { - this.tree = new MergedEntryMappingTree(this.mainIndex.getTree(), this.libIndex.getTree()); - } - return this.tree; } } From b4662b2e9b7118fdfc05bd307d0ab43d49d095be Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Fri, 12 Dec 2025 17:50:09 -0800 Subject: [PATCH 13/14] fix javadoc typo --- .../java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java b/enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java index c2a47268a..597ea1ce6 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java +++ b/enigma/src/main/java/org/quiltmc/enigma/util/UnmodifiableCombinedMap.java @@ -10,7 +10,7 @@ /** * An unmodifiable view of two backing maps. * - *

Only intended for maps no {@link #keySet()} overlap. Behavior is undefined in the case of overlap. + *

Only intended for maps with no {@link #keySet()} overlap. Behavior is undefined in the case of overlap. * * @param the type of keys * @param the type of values From 43aa971b75a365b6144abbd299968acd5e8a1b0a Mon Sep 17 00:00:00 2001 From: supersaiyansubtlety Date: Fri, 12 Dec 2025 18:17:12 -0800 Subject: [PATCH 14/14] fix CombinedCollection's interator implementation --- .../enigma/util/CombinedCollection.java | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/enigma/src/main/java/org/quiltmc/enigma/util/CombinedCollection.java b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedCollection.java index 80a6fdf43..7912a213d 100644 --- a/enigma/src/main/java/org/quiltmc/enigma/util/CombinedCollection.java +++ b/enigma/src/main/java/org/quiltmc/enigma/util/CombinedCollection.java @@ -1,10 +1,10 @@ package org.quiltmc.enigma.util; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.util.Collection; import java.util.Iterator; -import java.util.NoSuchElementException; /** * A collection backed by two other collections. @@ -121,42 +121,31 @@ public void clear() { } private class CombinedIterator implements Iterator { - Iterator delegate = CombinedCollection.this.first.iterator(); + final Iterator first = CombinedCollection.this.first.iterator(); + final Iterator second = CombinedCollection.this.second.iterator(); - boolean iteratingFirst = true; + // null iff next has not been called + @Nullable + Iterator nextSource; @Override public boolean hasNext() { - final boolean currentHasNext = this.delegate.hasNext(); - if (currentHasNext) { - return true; - } else { - if (this.iteratingFirst) { - this.delegate = CombinedCollection.this.second.iterator(); - this.iteratingFirst = false; - - return this.delegate.hasNext(); - } else { - return false; - } - } + return this.first.hasNext() || this.second.hasNext(); } @Override public T next() { - if (!this.hasNext()) { - throw new NoSuchElementException(); - } - - return this.delegate.next(); + this.nextSource = this.first.hasNext() ? this.first : this.second; + return this.nextSource.next(); } - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void remove() { - // update delegate - this.hasNext(); - this.delegate.remove(); + if (this.nextSource == null) { + throw new IllegalStateException("remove called before any calls to next!"); + } else { + this.nextSource.remove(); + } } } }