summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis/index
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/index')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java77
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java109
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java40
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java97
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java165
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java24
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java9
9 files changed, 687 insertions, 0 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
new file mode 100644
index 0000000..e1903d9
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
@@ -0,0 +1,77 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.Maps;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryResolver;
6import cuchaz.enigma.translation.representation.AccessFlags;
7import cuchaz.enigma.translation.representation.entry.MethodEntry;
8
9import javax.annotation.Nullable;
10import java.util.Collection;
11import java.util.Map;
12
13public class BridgeMethodIndex implements JarIndexer, RemappableIndex {
14 private final EntryIndex entryIndex;
15 private final ReferenceIndex referenceIndex;
16
17 private Map<MethodEntry, MethodEntry> accessedToBridge = Maps.newHashMap();
18
19 public BridgeMethodIndex(EntryIndex entryIndex, ReferenceIndex referenceIndex) {
20 this.entryIndex = entryIndex;
21 this.referenceIndex = referenceIndex;
22 }
23
24 @Override
25 public void remap(Translator translator) {
26 accessedToBridge = translator.translate(accessedToBridge);
27 }
28
29 @Override
30 public BridgeMethodIndex remapped(Translator translator) {
31 BridgeMethodIndex index = new BridgeMethodIndex(entryIndex, referenceIndex);
32 index.accessedToBridge = translator.translate(accessedToBridge);
33
34 return index;
35 }
36
37 @Override
38 public void processIndex(EntryResolver resolver) {
39 // look for access and bridged methods
40 for (MethodEntry methodEntry : entryIndex.getMethods()) {
41 AccessFlags access = entryIndex.getMethodAccess(methodEntry);
42 if (access == null || !access.isSynthetic()) {
43 continue;
44 }
45
46 indexSyntheticMethod(methodEntry, access);
47 }
48 }
49
50 private void indexSyntheticMethod(MethodEntry syntheticMethod, AccessFlags access) {
51 if (access.isBridge()) {
52 MethodEntry accessedMethod = findAccessMethod(syntheticMethod);
53 if (accessedMethod != null) {
54 accessedToBridge.put(accessedMethod, syntheticMethod);
55 }
56 }
57 }
58
59 private MethodEntry findAccessMethod(MethodEntry method) {
60 // we want to find all compiler-added methods that directly call another with no processing
61
62 // get all the methods that we call
63 final Collection<MethodEntry> referencedMethods = referenceIndex.getMethodsReferencedBy(method);
64
65 // is there just one?
66 if (referencedMethods.size() != 1) {
67 return null;
68 }
69
70 return referencedMethods.stream().findFirst().orElse(null);
71 }
72
73 @Nullable
74 public MethodEntry getBridgeFromAccessed(MethodEntry entry) {
75 return accessedToBridge.get(entry);
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
new file mode 100644
index 0000000..55bfbc2
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
@@ -0,0 +1,109 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.AccessFlags;
5import cuchaz.enigma.translation.representation.entry.*;
6
7import javax.annotation.Nullable;
8import java.util.Collection;
9import java.util.HashMap;
10import java.util.Map;
11
12public class EntryIndex implements JarIndexer, RemappableIndex {
13 private Map<ClassEntry, AccessFlags> classes = new HashMap<>();
14 private Map<FieldEntry, AccessFlags> fields = new HashMap<>();
15 private Map<MethodEntry, AccessFlags> methods = new HashMap<>();
16
17 @Override
18 public void remap(Translator translator) {
19 classes = translator.translateKeys(classes);
20 fields = translator.translateKeys(fields);
21 methods = translator.translateKeys(methods);
22 }
23
24 @Override
25 public EntryIndex remapped(Translator translator) {
26 EntryIndex index = new EntryIndex();
27 index.classes = translator.translateKeys(classes);
28 index.fields = translator.translateKeys(fields);
29 index.methods = translator.translateKeys(methods);
30
31 return index;
32 }
33
34 @Override
35 public void indexClass(ClassDefEntry classEntry) {
36 classes.put(classEntry, classEntry.getAccess());
37 }
38
39 @Override
40 public void indexMethod(MethodDefEntry methodEntry) {
41 methods.put(methodEntry, methodEntry.getAccess());
42 }
43
44 @Override
45 public void indexField(FieldDefEntry fieldEntry) {
46 fields.put(fieldEntry, fieldEntry.getAccess());
47 }
48
49 public boolean hasClass(ClassEntry entry) {
50 return classes.containsKey(entry);
51 }
52
53 public boolean hasMethod(MethodEntry entry) {
54 return methods.containsKey(entry);
55 }
56
57 public boolean hasField(FieldEntry entry) {
58 return fields.containsKey(entry);
59 }
60
61 public boolean hasEntry(Entry<?> entry) {
62 if (entry instanceof ClassEntry) {
63 return hasClass((ClassEntry) entry);
64 } else if (entry instanceof MethodEntry) {
65 return hasMethod((MethodEntry) entry);
66 } else if (entry instanceof FieldEntry) {
67 return hasField((FieldEntry) entry);
68 } else if (entry instanceof LocalVariableEntry) {
69 return hasMethod(((LocalVariableEntry) entry).getParent());
70 }
71
72 return false;
73 }
74
75 @Nullable
76 public AccessFlags getMethodAccess(MethodEntry entry) {
77 return methods.get(entry);
78 }
79
80 @Nullable
81 public AccessFlags getFieldAccess(FieldEntry entry) {
82 return fields.get(entry);
83 }
84
85 @Nullable
86 public AccessFlags getEntryAccess(Entry<?> entry) {
87 if (entry instanceof MethodEntry) {
88 return getMethodAccess((MethodEntry) entry);
89 } else if (entry instanceof FieldEntry) {
90 return getFieldAccess((FieldEntry) entry);
91 } else if (entry instanceof LocalVariableEntry) {
92 return getMethodAccess(((LocalVariableEntry) entry).getParent());
93 }
94
95 return null;
96 }
97
98 public Collection<ClassEntry> getClasses() {
99 return classes.keySet();
100 }
101
102 public Collection<MethodEntry> getMethods() {
103 return methods.keySet();
104 }
105
106 public Collection<FieldEntry> getFields() {
107 return fields.keySet();
108 }
109}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
new file mode 100644
index 0000000..f9cb23c
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
@@ -0,0 +1,40 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
4import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
5import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
6import org.objectweb.asm.ClassVisitor;
7import org.objectweb.asm.FieldVisitor;
8import org.objectweb.asm.MethodVisitor;
9
10public class IndexClassVisitor extends ClassVisitor {
11 private final JarIndexer indexer;
12 private ClassDefEntry classEntry;
13
14 public IndexClassVisitor(JarIndex indexer, int api) {
15 super(api);
16 this.indexer = indexer;
17 }
18
19 @Override
20 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
21 classEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces);
22 indexer.indexClass(classEntry);
23
24 super.visit(version, access, name, signature, superName, interfaces);
25 }
26
27 @Override
28 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
29 indexer.indexField(FieldDefEntry.parse(classEntry, access, name, desc, signature));
30
31 return super.visitField(access, name, desc, signature, value);
32 }
33
34 @Override
35 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
36 indexer.indexMethod(MethodDefEntry.parse(classEntry, access, name, desc, signature));
37
38 return super.visitMethod(access, name, desc, signature, exceptions);
39 }
40}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
new file mode 100644
index 0000000..ba5d3b6
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
@@ -0,0 +1,83 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4import cuchaz.enigma.translation.representation.MethodDescriptor;
5import cuchaz.enigma.translation.representation.Signature;
6import cuchaz.enigma.translation.representation.entry.ClassEntry;
7import cuchaz.enigma.translation.representation.entry.FieldEntry;
8import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
10import org.objectweb.asm.ClassVisitor;
11import org.objectweb.asm.Handle;
12import org.objectweb.asm.MethodVisitor;
13import org.objectweb.asm.Opcodes;
14
15public class IndexReferenceVisitor extends ClassVisitor {
16 private final JarIndexer indexer;
17 private ClassEntry classEntry;
18
19 public IndexReferenceVisitor(JarIndexer indexer, int api) {
20 super(api);
21 this.indexer = indexer;
22 }
23
24 @Override
25 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
26 this.classEntry = new ClassEntry(name);
27 }
28
29 @Override
30 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
31 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
32 return new Method(this.indexer, entry, this.api);
33 }
34
35 private static class Method extends MethodVisitor {
36 private final JarIndexer indexer;
37 private final MethodDefEntry callerEntry;
38
39 public Method(JarIndexer indexer, MethodDefEntry callerEntry, int api) {
40 super(api);
41 this.indexer = indexer;
42 this.callerEntry = callerEntry;
43 }
44
45 @Override
46 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
47 FieldEntry fieldEntry = FieldEntry.parse(owner, name, desc);
48 this.indexer.indexFieldReference(callerEntry, fieldEntry);
49 }
50
51 @Override
52 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
53 MethodEntry methodEntry = MethodEntry.parse(owner, name, desc);
54 this.indexer.indexMethodReference(callerEntry, methodEntry);
55 }
56
57 @Override
58 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
59 for (Object bsmArg : bsmArgs) {
60 if (bsmArg instanceof Handle) {
61 Handle handle = (Handle) bsmArg;
62 switch (handle.getTag()) {
63 case Opcodes.H_GETFIELD:
64 case Opcodes.H_GETSTATIC:
65 case Opcodes.H_PUTFIELD:
66 case Opcodes.H_PUTSTATIC:
67 FieldEntry fieldEntry = FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
68 this.indexer.indexFieldReference(callerEntry, fieldEntry);
69 break;
70 case Opcodes.H_INVOKEINTERFACE:
71 case Opcodes.H_INVOKESPECIAL:
72 case Opcodes.H_INVOKESTATIC:
73 case Opcodes.H_INVOKEVIRTUAL:
74 case Opcodes.H_NEWINVOKESPECIAL:
75 MethodEntry methodEntry = MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
76 this.indexer.indexMethodReference(callerEntry, methodEntry);
77 break;
78 }
79 }
80 }
81 }
82 }
83}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
new file mode 100644
index 0000000..d165cc8
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
@@ -0,0 +1,97 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20
21import java.util.Collection;
22import java.util.LinkedList;
23import java.util.Set;
24
25public class InheritanceIndex implements JarIndexer, RemappableIndex {
26 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create();
27 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create();
28
29 @Override
30 public void remap(Translator translator) {
31 classChildren = translator.translate(classChildren);
32 classParents = translator.translate(classParents);
33 }
34
35 @Override
36 public InheritanceIndex remapped(Translator translator) {
37 InheritanceIndex index = new InheritanceIndex();
38 index.classParents = translator.translate(classParents);
39 index.classChildren = translator.translate(classChildren);
40
41 return index;
42 }
43
44 @Override
45 public void indexClass(ClassDefEntry classEntry) {
46 ClassEntry superClass = classEntry.getSuperClass();
47 if (superClass != null) {
48 indexParent(classEntry, superClass);
49 }
50
51 for (ClassEntry interfaceEntry : classEntry.getInterfaces()) {
52 indexParent(classEntry, interfaceEntry);
53 }
54 }
55
56 private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) {
57 if (childEntry.isJre() || parentEntry.isJre()) {
58 return;
59 }
60 classParents.put(childEntry, parentEntry);
61 classChildren.put(parentEntry, childEntry);
62 }
63
64 public Collection<ClassEntry> getParents(ClassEntry classEntry) {
65 return classParents.get(classEntry);
66 }
67
68 public Collection<ClassEntry> getChildren(ClassEntry classEntry) {
69 return classChildren.get(classEntry);
70 }
71
72 public Set<ClassEntry> getAncestors(ClassEntry classEntry) {
73 Set<ClassEntry> ancestors = Sets.newHashSet();
74
75 LinkedList<ClassEntry> ancestorQueue = new LinkedList<>();
76 ancestorQueue.push(classEntry);
77
78 while (!ancestorQueue.isEmpty()) {
79 ClassEntry ancestor = ancestorQueue.pop();
80 Collection<ClassEntry> parents = getParents(ancestor);
81
82 parents.forEach(ancestorQueue::push);
83 ancestors.addAll(parents);
84 }
85
86 return ancestors;
87 }
88
89 public boolean isParent(ClassEntry classEntry) {
90 return classChildren.containsKey(classEntry);
91 }
92
93 public boolean hasParents(ClassEntry classEntry) {
94 Collection<ClassEntry> parents = classParents.get(classEntry);
95 return parents != null && !parents.isEmpty();
96 }
97}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
new file mode 100644
index 0000000..0880244
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
@@ -0,0 +1,165 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import cuchaz.enigma.analysis.ParsedJar;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.mapping.IndexEntryResolver;
20import cuchaz.enigma.translation.representation.entry.*;
21import org.objectweb.asm.ClassReader;
22import org.objectweb.asm.Opcodes;
23
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.function.Consumer;
27
28public class JarIndex implements JarIndexer, RemappableIndex {
29 private final EntryIndex entryIndex;
30 private final InheritanceIndex inheritanceIndex;
31 private final ReferenceIndex referenceIndex;
32 private final BridgeMethodIndex bridgeMethodIndex;
33 private final EntryResolver entryResolver;
34
35 private final Collection<JarIndexer> indexers;
36
37 private final Multimap<String, MethodDefEntry> methodImplementations = HashMultimap.create();
38
39 public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex) {
40 this.entryIndex = entryIndex;
41 this.inheritanceIndex = inheritanceIndex;
42 this.referenceIndex = referenceIndex;
43 this.bridgeMethodIndex = bridgeMethodIndex;
44 this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
45 this.entryResolver = new IndexEntryResolver(this);
46 }
47
48 public static JarIndex empty() {
49 EntryIndex entryIndex = new EntryIndex();
50 InheritanceIndex inheritanceIndex = new InheritanceIndex();
51 ReferenceIndex referenceIndex = new ReferenceIndex();
52 BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, referenceIndex);
53 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
54 }
55
56 @Override
57 public void remap(Translator translator) {
58 entryIndex.remap(translator);
59 inheritanceIndex.remap(translator);
60 bridgeMethodIndex.remap(translator);
61 }
62
63 @Override
64 public JarIndex remapped(Translator translator) {
65 EntryIndex entryIndex = this.entryIndex.remapped(translator);
66 InheritanceIndex inheritanceIndex = this.inheritanceIndex.remapped(translator);
67 BridgeMethodIndex bridgeMethodIndex = this.bridgeMethodIndex.remapped(translator);
68
69 JarIndex remappedIndex = new JarIndex(entryIndex, inheritanceIndex, this.referenceIndex, bridgeMethodIndex);
70 remappedIndex.methodImplementations.putAll(methodImplementations);
71
72 return remappedIndex;
73 }
74
75 public void indexJar(ParsedJar jar, Consumer<String> progress) {
76 progress.accept("Indexing entries (1/3)");
77 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE);
78
79 progress.accept("Indexing entry references (2/3)");
80 jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES);
81
82 progress.accept("Processing index (3/3)");
83 processIndex(entryResolver);
84 }
85
86 @Override
87 public void processIndex(EntryResolver resolver) {
88 indexers.forEach(indexer -> indexer.processIndex(entryResolver));
89 }
90
91 @Override
92 public void indexClass(ClassDefEntry classEntry) {
93 if (classEntry.isJre()) {
94 return;
95 }
96
97 for (ClassEntry interfaceEntry : classEntry.getInterfaces()) {
98 if (classEntry.equals(interfaceEntry)) {
99 throw new IllegalArgumentException("Class cannot be its own interface! " + classEntry);
100 }
101 }
102
103 indexers.forEach(indexer -> indexer.indexClass(classEntry));
104 }
105
106 @Override
107 public void indexField(FieldDefEntry fieldEntry) {
108 if (fieldEntry.getParent().isJre()) {
109 return;
110 }
111
112 indexers.forEach(indexer -> indexer.indexField(fieldEntry));
113 }
114
115 @Override
116 public void indexMethod(MethodDefEntry methodEntry) {
117 if (methodEntry.getParent().isJre()) {
118 return;
119 }
120
121 indexers.forEach(indexer -> indexer.indexMethod(methodEntry));
122
123 if (!methodEntry.isConstructor()) {
124 methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry);
125 }
126 }
127
128 @Override
129 public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
130 if (callerEntry.getParent().isJre()) {
131 return;
132 }
133
134 indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry));
135 }
136
137 @Override
138 public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
139 if (callerEntry.getParent().isJre()) {
140 return;
141 }
142
143 indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry));
144 }
145
146 public EntryIndex getEntryIndex() {
147 return entryIndex;
148 }
149
150 public InheritanceIndex getInheritanceIndex() {
151 return this.inheritanceIndex;
152 }
153
154 public ReferenceIndex getReferenceIndex() {
155 return referenceIndex;
156 }
157
158 public BridgeMethodIndex getBridgeMethodIndex() {
159 return bridgeMethodIndex;
160 }
161
162 public EntryResolver getEntryResolver() {
163 return entryResolver;
164 }
165}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
new file mode 100644
index 0000000..a087e59
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
@@ -0,0 +1,24 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.mapping.EntryResolver;
4import cuchaz.enigma.translation.representation.entry.*;
5
6public interface JarIndexer {
7 default void indexClass(ClassDefEntry classEntry) {
8 }
9
10 default void indexField(FieldDefEntry fieldEntry) {
11 }
12
13 default void indexMethod(MethodDefEntry methodEntry) {
14 }
15
16 default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
17 }
18
19 default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
20 }
21
22 default void processIndex(EntryResolver resolver) {
23 }
24}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
new file mode 100644
index 0000000..ac11da4
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
@@ -0,0 +1,83 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Multimap;
5import cuchaz.enigma.analysis.EntryReference;
6import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.entry.*;
9
10import java.util.Collection;
11import java.util.Map;
12
13public class ReferenceIndex implements JarIndexer {
14 private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create();
15
16 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> referencesToMethods = HashMultimap.create();
17 private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> referencesToClasses = HashMultimap.create();
18 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> referencesToFields = HashMultimap.create();
19
20 @Override
21 public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
22 referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
23 methodReferences.put(callerEntry, referencedEntry);
24
25 if (referencedEntry.isConstructor()) {
26 ClassEntry referencedClass = referencedEntry.getParent();
27 referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry));
28 }
29 }
30
31 @Override
32 public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
33 referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
34 }
35
36 @Override
37 public void processIndex(EntryResolver resolver) {
38 methodReferences = resolveReferences(resolver, methodReferences);
39 referencesToMethods = resolveReferencesTo(resolver, referencesToMethods);
40 referencesToClasses = resolveReferencesTo(resolver, referencesToClasses);
41 referencesToFields = resolveReferencesTo(resolver, referencesToFields);
42 }
43
44 private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> resolveReferences(EntryResolver resolver, Multimap<K, V> multimap) {
45 Multimap<K, V> resolved = HashMultimap.create();
46 for (Map.Entry<K, V> entry : multimap.entries()) {
47 resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue()));
48 }
49 return resolved;
50 }
51
52 private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> resolveReferencesTo(EntryResolver resolver, Multimap<E, EntryReference<E, C>> multimap) {
53 Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create();
54 for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) {
55 resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue()));
56 }
57 return resolved;
58 }
59
60 private <E extends Entry<?>> E resolve(EntryResolver resolver, E entry) {
61 return resolver.resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST);
62 }
63
64 private <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> resolve(EntryResolver resolver, EntryReference<E, C> reference) {
65 return resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
66 }
67
68 public Collection<MethodEntry> getMethodsReferencedBy(MethodEntry entry) {
69 return methodReferences.get(entry);
70 }
71
72 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getReferencesToField(FieldEntry entry) {
73 return referencesToFields.get(entry);
74 }
75
76 public Collection<EntryReference<ClassEntry, MethodDefEntry>> getReferencesToClass(ClassEntry entry) {
77 return referencesToClasses.get(entry);
78 }
79
80 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferencesToMethod(MethodEntry entry) {
81 return referencesToMethods.get(entry);
82 }
83}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
new file mode 100644
index 0000000..537e772
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
@@ -0,0 +1,9 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.Translator;
4
5public interface RemappableIndex {
6 void remap(Translator translator);
7
8 RemappableIndex remapped(Translator translator);
9}