Skip to content

Commit 42ddbd9

Browse files
Fix 91 (#306)
* OUT OF MEMORY when trying to run all tests add JavaClassProvider which provied java.* classes add CombinedJarIndex which indexes main jar and libraries added missing translation for "progress.jar.indexing.libraries" * make methods that trace back to non-jar methods non-renamable * checkstyle * filter out unreferenced classNames for LibrariesJarIndex and CombinedJarIndex * add ReferenceIndex::getFieldsReferencedBy keep lib classes whose fields or methods are referenced in the main jar * exclude java/io/PrintStream from JavaClassProvider check if method is indexed in EnigmaProject::isLibraryMethodOverride * update asm (AnalyzerExceptions persist) * use ClasspathClassProvider instead of JavaClassProvider to avoid AnalyzerExceptions; BREAK #274 * stop excluding Object from InheritanceIndex and update tests; fix #91 * update TODO * try finding child node instead of assuming it's first * add clarifying comment to UN_ANALYZABLE_CLASS_NAMES Co-authored-by: ix0rai <[email protected]> * add jar index javadocs --------- Co-authored-by: ix0rai <[email protected]>
1 parent b3bd7b1 commit 42ddbd9

File tree

21 files changed

+464
-67
lines changed

21 files changed

+464
-67
lines changed

enigma-server/src/main/java/org/quiltmc/enigma/network/DedicatedEnigmaServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ public static void main(String[] args) {
124124

125125
EntryRemapper mappings;
126126
if (!Files.exists(mappingsFile)) {
127-
mappings = EntryRemapper.mapped(enigma, project.getJarIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), new HashEntryTree<>(), enigma.getNameProposalServices());
127+
mappings = EntryRemapper.mapped(enigma, project.getCombinedIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), new HashEntryTree<>(), enigma.getNameProposalServices());
128128
} else {
129129
Logger.info("Reading mappings...");
130-
mappings = EntryRemapper.mapped(enigma, project.getJarIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), readWriteService.get().read(mappingsFile), enigma.getNameProposalServices());
130+
mappings = EntryRemapper.mapped(enigma, project.getCombinedIndex(), project.getMappingsIndex(), project.getRemapper().getJarProposedMappings(), readWriteService.get().read(mappingsFile), enigma.getNameProposalServices());
131131
}
132132

133133
PrintWriter log = new PrintWriter(Files.newBufferedWriter(logFile));

enigma-swing/src/main/java/org/quiltmc/enigma/gui/GuiController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ public void createClient(String username, String ip, int port, char[] password)
686686
}
687687

688688
public void createServer(String username, int port, char[] password) throws IOException {
689-
this.server = new IntegratedEnigmaServer(this.project.getJarChecksum(), password, EntryRemapper.mapped(this.project.getEnigma(), this.project.getJarIndex(), this.project.getMappingsIndex(), new HashEntryTree<>(this.project.getRemapper().getJarProposedMappings()), new HashEntryTree<>(this.project.getRemapper().getDeobfMappings()), this.project.getEnigma().getNameProposalServices()), port);
689+
this.server = new IntegratedEnigmaServer(this.project.getJarChecksum(), password, EntryRemapper.mapped(this.project.getEnigma(), this.project.getCombinedIndex(), this.project.getMappingsIndex(), new HashEntryTree<>(this.project.getRemapper().getJarProposedMappings()), new HashEntryTree<>(this.project.getRemapper().getDeobfMappings()), this.project.getEnigma().getNameProposalServices()), port);
690690
this.server.start();
691691
this.client = new IntegratedEnigmaClient(this, "127.0.0.1", port);
692692
this.client.connect();

enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java

Lines changed: 107 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
package org.quiltmc.enigma.api;
22

3+
import com.google.common.base.Predicates;
4+
import com.google.common.collect.Streams;
35
import com.google.common.io.MoreFiles;
4-
import org.quiltmc.enigma.api.analysis.index.jar.JarIndex;
6+
import org.objectweb.asm.tree.ClassNode;
7+
import org.quiltmc.enigma.api.analysis.index.jar.CombinedJarIndex;
8+
import org.quiltmc.enigma.api.analysis.index.jar.EntryIndex;
9+
import org.quiltmc.enigma.api.analysis.index.jar.InheritanceIndex;
510
import org.quiltmc.enigma.api.analysis.index.jar.LibrariesJarIndex;
611
import org.quiltmc.enigma.api.analysis.index.jar.MainJarIndex;
12+
import org.quiltmc.enigma.api.analysis.index.jar.ReferenceIndex;
713
import org.quiltmc.enigma.api.analysis.index.mapping.MappingsIndex;
14+
import org.quiltmc.enigma.api.class_provider.ClasspathClassProvider;
815
import org.quiltmc.enigma.api.class_provider.ProjectClassProvider;
916
import org.quiltmc.enigma.api.translation.mapping.serde.MappingParseException;
17+
import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry;
18+
import org.quiltmc.enigma.api.translation.representation.entry.FieldEntry;
19+
import org.quiltmc.enigma.api.translation.representation.entry.MethodEntry;
1020
import org.quiltmc.enigma.impl.analysis.ClassLoaderClassProvider;
1121
import org.quiltmc.enigma.api.service.EnigmaService;
1222
import org.quiltmc.enigma.api.service.EnigmaServiceContext;
@@ -25,6 +35,8 @@
2535
import org.quiltmc.enigma.api.translation.mapping.tree.EntryTree;
2636
import org.quiltmc.enigma.api.translation.mapping.tree.HashEntryTree;
2737
import org.quiltmc.enigma.api.translation.representation.entry.Entry;
38+
import org.quiltmc.enigma.impl.analysis.index.AbstractJarIndex;
39+
import org.quiltmc.enigma.impl.analysis.index.IndexClassVisitor;
2840
import org.quiltmc.enigma.util.Either;
2941
import org.quiltmc.enigma.util.I18n;
3042
import org.quiltmc.enigma.util.Utils;
@@ -43,15 +55,20 @@
4355
import java.nio.file.attribute.BasicFileAttributes;
4456
import java.sql.DriverManager;
4557
import java.util.ArrayList;
58+
import java.util.Collection;
4659
import java.util.Collections;
4760
import java.util.HashSet;
4861
import java.util.List;
4962
import java.util.Map;
63+
import java.util.Objects;
5064
import java.util.Optional;
5165
import java.util.Properties;
5266
import java.util.ServiceLoader;
5367
import java.util.Set;
5468
import java.util.concurrent.atomic.AtomicReference;
69+
import java.util.function.Predicate;
70+
import java.util.stream.Collectors;
71+
import java.util.stream.Stream;
5572

5673
public class Enigma {
5774
public static final String NAME = "Enigma";
@@ -80,19 +97,27 @@ public static Builder builder() {
8097

8198
public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, ProgressListener progress) throws IOException {
8299
JarClassProvider jarClassProvider = new JarClassProvider(path);
83-
JarIndex index = MainJarIndex.empty();
84-
JarIndex libIndex = LibrariesJarIndex.empty();
100+
AbstractJarIndex jarIndex = MainJarIndex.empty();
101+
AbstractJarIndex libIndex = LibrariesJarIndex.empty();
102+
AbstractJarIndex comboIndex = CombinedJarIndex.empty();
85103

86104
ClassLoaderClassProvider jreProvider = new ClassLoaderClassProvider(DriverManager.class.getClassLoader());
87-
CombiningClassProvider librariesProvider = new CombiningClassProvider(jreProvider, libraryClassProvider);
88-
ClassProvider mainProjectProvider = new ObfuscationFixClassProvider(new CachingClassProvider(jarClassProvider), index);
105+
ClasspathClassProvider javaClassProvider = new ClasspathClassProvider();
106+
CombiningClassProvider librariesProvider = new CombiningClassProvider(jreProvider, javaClassProvider, libraryClassProvider);
107+
ClassProvider mainProjectProvider = new ObfuscationFixClassProvider(new CachingClassProvider(jarClassProvider), jarIndex);
89108
ProjectClassProvider projectClassProvider = new ProjectClassProvider(mainProjectProvider, librariesProvider);
90109

91110
// main index
92-
this.index(index, projectClassProvider, progress);
111+
this.index(jarIndex, projectClassProvider, progress, "jar", false, null);
112+
113+
// TODO make filtering toggleable with arg once JavaClassProvider is used
114+
final Predicate<String> mainReferencedPredicate = this.createMainReferencedPredicate(jarIndex, projectClassProvider);
93115

94116
// lib index
95-
this.index(libIndex, projectClassProvider, progress);
117+
this.index(libIndex, projectClassProvider, progress, "jar", true, mainReferencedPredicate);
118+
119+
// combined main and lib index
120+
this.index(comboIndex, projectClassProvider, progress, "combined", true, mainReferencedPredicate);
96121

97122
// name proposal
98123
var nameProposalServices = this.getNameProposalServices();
@@ -103,7 +128,7 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog
103128
int j = 1;
104129
for (var service : nameProposalServices) {
105130
progress.step(j++, I18n.translateFormatted("progress.jar.name_proposal.proposer", service.getId()));
106-
Map<Entry<?>, EntryMapping> proposed = service.getProposedNames(this, index);
131+
Map<Entry<?>, EntryMapping> proposed = service.getProposedNames(this, jarIndex);
107132

108133
if (proposed != null) {
109134
for (var entry : proposed.entrySet()) {
@@ -118,23 +143,89 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog
118143
MappingsIndex mappingsIndex = MappingsIndex.empty();
119144
mappingsIndex.indexMappings(proposedNames, progress);
120145

121-
return new EnigmaProject(this, path, mainProjectProvider, index, libIndex, mappingsIndex, proposedNames, Utils.zipSha1(path));
146+
return new EnigmaProject(this, path, mainProjectProvider, jarIndex, libIndex, comboIndex, mappingsIndex, proposedNames, Utils.zipSha1(path));
122147
}
123148

124-
private void index(JarIndex index, ProjectClassProvider classProvider, ProgressListener progress) {
125-
boolean libraries = index instanceof LibrariesJarIndex;
126-
String progressKey = libraries ? "libs" : "jar";
127-
index.indexJar(classProvider, progress);
149+
private Predicate<String> createMainReferencedPredicate(AbstractJarIndex mainIndex, ProjectClassProvider classProvider) {
150+
final EntryIndex mainEntryIndex = mainIndex.getIndex(EntryIndex.class);
151+
152+
final EntryIndex entryIndex = new EntryIndex();
153+
final ReferenceIndex referenceIndex = new ReferenceIndex();
154+
final InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex);
155+
156+
final Collection<String> allClassNames = classProvider.getClassNames();
157+
for (final String className : allClassNames) {
158+
final ClassNode classNode = Objects.requireNonNull(classProvider.get(className));
159+
classNode.accept(new IndexClassVisitor(entryIndex, Enigma.ASM_VERSION));
160+
classNode.accept(new IndexClassVisitor(referenceIndex, Enigma.ASM_VERSION));
161+
classNode.accept(new IndexClassVisitor(inheritanceIndex, Enigma.ASM_VERSION));
162+
}
163+
164+
return className -> {
165+
final ClassEntry classEntry = new ClassEntry(className);
166+
if (mainEntryIndex.hasClass(classEntry)) {
167+
return true;
168+
}
169+
170+
if (inheritanceIndex.getChildren(classEntry).stream().anyMatch(mainEntryIndex::hasClass)) {
171+
return true;
172+
}
173+
174+
final boolean typeReferenced = Streams
175+
.concat(
176+
referenceIndex.getReferencesToClass(classEntry).stream(),
177+
referenceIndex.getMethodTypeReferencesToClass(classEntry).stream(),
178+
referenceIndex.getFieldTypeReferencesToClass(classEntry).stream()
179+
)
180+
.anyMatch(reference ->
181+
mainEntryIndex.hasClass(reference.entry) || mainEntryIndex.hasEntry(reference.context)
182+
);
183+
184+
if (typeReferenced) {
185+
return true;
186+
}
187+
188+
final List<MethodEntry> mainMethods = mainIndex.getChildrenByClass().values().stream()
189+
.flatMap(entry -> entry instanceof MethodEntry method ? Stream.of(method) : Stream.empty())
190+
.toList();
191+
192+
final boolean methodReferenced = mainMethods.stream()
193+
.flatMap(method -> referenceIndex.getMethodsReferencedBy(method).stream())
194+
.map(MethodEntry::getParent)
195+
.anyMatch(classEntry::equals);
196+
if (methodReferenced) {
197+
return true;
198+
}
199+
200+
// field referenced
201+
return mainMethods.stream()
202+
.flatMap(method -> referenceIndex.getFieldsReferencedBy(method).stream())
203+
.map(FieldEntry::getParent)
204+
.anyMatch(classEntry::equals);
205+
};
206+
}
207+
208+
private void index(
209+
AbstractJarIndex index, ProjectClassProvider classProvider, ProgressListener progress, String progressKey,
210+
boolean includesLibraries, @Nullable Predicate<String> classNameFilter
211+
) {
212+
if (classNameFilter == null) {
213+
index.indexJar(classProvider, progress);
214+
classNameFilter = Predicates.alwaysTrue();
215+
} else {
216+
index.indexJar(classProvider, progress, classNameFilter);
217+
}
128218

129219
List<JarIndexerService> indexers = this.services.get(JarIndexerService.TYPE);
130220
progress.init(indexers.size(), I18n.translate("progress." + progressKey + ".custom_indexing"));
131221

132222
int i = 1;
133223
for (var service : indexers) {
134-
if (!(libraries && !service.shouldIndexLibraries())) {
224+
if (!(includesLibraries && !service.shouldIndexLibraries())) {
135225
progress.step(i++, I18n.translateFormatted("progress." + progressKey + ".custom_indexing.indexer", service.getId()));
136-
var names = libraries ? classProvider.getLibraryClassNames() : classProvider.getMainClassNames();
137-
Set<String> scope = new HashSet<>(names);
226+
Set<String> scope = index.getIndexableClassNames(classProvider).stream()
227+
.filter(classNameFilter)
228+
.collect(Collectors.toCollection(HashSet::new));
138229
service.acceptJar(scope, classProvider, index);
139230
}
140231
}

enigma/src/main/java/org/quiltmc/enigma/api/EnigmaProject.java

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import org.quiltmc.enigma.api.analysis.index.mapping.MappingsIndex;
1010
import org.quiltmc.enigma.api.service.ObfuscationTestService;
1111
import org.quiltmc.enigma.api.source.TokenType;
12+
import org.quiltmc.enigma.api.translation.mapping.EntryResolver;
13+
import org.quiltmc.enigma.api.translation.mapping.ResolutionStrategy;
1214
import org.quiltmc.enigma.api.translation.mapping.tree.EntryTreeUtil;
1315
import org.quiltmc.enigma.api.translation.mapping.tree.HashEntryTree;
1416
import org.quiltmc.enigma.impl.bytecode.translator.TranslationClassVisitor;
@@ -41,9 +43,11 @@
4143
import java.nio.file.Files;
4244
import java.nio.file.Path;
4345
import java.util.Collection;
46+
import java.util.HashMap;
4447
import java.util.List;
4548
import java.util.Map;
4649
import java.util.Objects;
50+
import java.util.Set;
4751
import java.util.concurrent.atomic.AtomicInteger;
4852
import java.util.jar.JarEntry;
4953
import java.util.jar.JarOutputStream;
@@ -56,22 +60,25 @@ public class EnigmaProject {
5660
private final ClassProvider classProvider;
5761
private final JarIndex jarIndex;
5862
private final JarIndex libIndex;
63+
private final JarIndex combinedIndex;
5964
private final byte[] jarChecksum;
65+
private final Map<MethodEntry, Boolean> libraryMethodOverrideCache = new HashMap<>();
6066

6167
private EntryRemapper remapper;
6268
private MappingsIndex mappingsIndex;
6369

64-
public EnigmaProject(Enigma enigma, Path jarPath, ClassProvider classProvider, JarIndex jarIndex, JarIndex libIndex, MappingsIndex mappingsIndex, EntryTree<EntryMapping> proposedNames, byte[] jarChecksum) {
70+
public EnigmaProject(Enigma enigma, Path jarPath, ClassProvider classProvider, JarIndex jarIndex, JarIndex libIndex, JarIndex combinedIndex, MappingsIndex mappingsIndex, EntryTree<EntryMapping> proposedNames, byte[] jarChecksum) {
6571
Preconditions.checkArgument(jarChecksum.length == 20);
6672
this.enigma = enigma;
6773
this.jarPath = jarPath;
6874
this.classProvider = classProvider;
6975
this.jarIndex = jarIndex;
7076
this.libIndex = libIndex;
77+
this.combinedIndex = combinedIndex;
7178
this.jarChecksum = jarChecksum;
7279

7380
this.mappingsIndex = mappingsIndex;
74-
this.remapper = EntryRemapper.mapped(enigma, jarIndex, this.mappingsIndex, proposedNames, new HashEntryTree<>(), this.enigma.getNameProposalServices());
81+
this.remapper = EntryRemapper.mapped(this.enigma, this.combinedIndex, this.mappingsIndex, proposedNames, new HashEntryTree<>(), this.enigma.getNameProposalServices());
7582
}
7683

7784
/**
@@ -90,12 +97,12 @@ public void setMappings(@Nullable EntryTree<EntryMapping> mappings, ProgressList
9097
EntryTree<EntryMapping> mergedTree = EntryTreeUtil.merge(jarProposedMappings, mappings);
9198

9299
this.mappingsIndex.indexMappings(mergedTree, progress);
93-
this.remapper = EntryRemapper.mapped(this.enigma, this.jarIndex, this.mappingsIndex, jarProposedMappings, mappings, this.enigma.getNameProposalServices());
100+
this.remapper = EntryRemapper.mapped(this.enigma, this.combinedIndex, this.mappingsIndex, jarProposedMappings, mappings, this.enigma.getNameProposalServices());
94101
} else if (!jarProposedMappings.isEmpty()) {
95102
this.mappingsIndex.indexMappings(jarProposedMappings, progress);
96-
this.remapper = EntryRemapper.mapped(this.enigma, this.jarIndex, this.mappingsIndex, jarProposedMappings, new HashEntryTree<>(), this.enigma.getNameProposalServices());
103+
this.remapper = EntryRemapper.mapped(this.enigma, this.combinedIndex, this.mappingsIndex, jarProposedMappings, new HashEntryTree<>(), this.enigma.getNameProposalServices());
97104
} else {
98-
this.remapper = EntryRemapper.empty(this.enigma, this.jarIndex, this.enigma.getNameProposalServices());
105+
this.remapper = EntryRemapper.empty(this.enigma, this.combinedIndex, this.enigma.getNameProposalServices());
99106
}
100107

101108
// update dynamically proposed names
@@ -114,10 +121,27 @@ public ClassProvider getClassProvider() {
114121
return this.classProvider;
115122
}
116123

124+
/**
125+
* Gets the index of the main jar of this project; the jar being mapped.
126+
*/
117127
public JarIndex getJarIndex() {
118128
return this.jarIndex;
119129
}
120130

131+
/**
132+
* Gets the index of the library jars of this project.
133+
*/
134+
public JarIndex getLibIndex() {
135+
return this.libIndex;
136+
}
137+
138+
/**
139+
* Gets the index of the main jar <em>and</em> library jars of this project.
140+
*/
141+
public JarIndex getCombinedIndex() {
142+
return this.combinedIndex;
143+
}
144+
121145
public MappingsIndex getMappingsIndex() {
122146
return this.mappingsIndex;
123147
}
@@ -199,6 +223,10 @@ public boolean isRenamable(Entry<?> obfEntry) {
199223
|| isEnumValueOfMethod(parent, obfMethodEntry))) {
200224
return false;
201225
}
226+
227+
if (this.isLibraryMethodOverride(obfMethodEntry)) {
228+
return false;
229+
}
202230
} else if (obfEntry instanceof LocalVariableEntry localEntry && !localEntry.isArgument()) {
203231
return false;
204232
} else if (obfEntry instanceof LocalVariableEntry localEntry && localEntry.isArgument()) {
@@ -216,6 +244,36 @@ public boolean isRenamable(Entry<?> obfEntry) {
216244
return this.jarIndex.getIndex(EntryIndex.class).hasEntry(obfEntry);
217245
}
218246

247+
private boolean isLibraryMethodOverride(MethodEntry methodEntry) {
248+
final Boolean cached = this.libraryMethodOverrideCache.get(methodEntry);
249+
if (cached != null) {
250+
return cached;
251+
} else {
252+
if (this.combinedIndex.getIndex(EntryIndex.class).hasMethod(methodEntry)) {
253+
final EntryResolver combinedResolver = this.combinedIndex.getEntryResolver();
254+
final Set<MethodEntry> equivalents = combinedResolver.resolveEquivalentMethods(methodEntry);
255+
final Set<MethodEntry> roots = equivalents.stream()
256+
.flatMap(equivalent -> combinedResolver.resolveEntry(equivalent, ResolutionStrategy.RESOLVE_ROOT).stream())
257+
.collect(Collectors.toSet());
258+
259+
final Set<MethodEntry> equivalentsAndRoots = Stream
260+
.concat(equivalents.stream(), roots.stream())
261+
.collect(Collectors.toSet());
262+
263+
final EntryIndex jarEntryIndex = this.jarIndex.getIndex(EntryIndex.class);
264+
final boolean anyNonJar = equivalentsAndRoots.stream().anyMatch(method -> !jarEntryIndex.hasMethod(method));
265+
266+
equivalentsAndRoots.forEach(method -> this.libraryMethodOverrideCache.put(method, anyNonJar));
267+
268+
return anyNonJar;
269+
} else {
270+
this.libraryMethodOverrideCache.put(methodEntry, false);
271+
272+
return false;
273+
}
274+
}
275+
}
276+
219277
private static boolean isEnumValueOfMethod(ClassDefEntry parent, MethodEntry method) {
220278
return parent != null && parent.isEnum() && method.getName().equals("valueOf") && method.getDesc().toString().equals("(Ljava/lang/String;)L" + parent.getFullName() + ";");
221279
}

0 commit comments

Comments
 (0)