diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/index')
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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.Maps; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 6 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 8 | |||
| 9 | import javax.annotation.Nullable; | ||
| 10 | import java.util.Collection; | ||
| 11 | import java.util.Map; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 6 | |||
| 7 | import javax.annotation.Nullable; | ||
| 8 | import java.util.Collection; | ||
| 9 | import java.util.HashMap; | ||
| 10 | import java.util.Map; | ||
| 11 | |||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 6 | import org.objectweb.asm.ClassVisitor; | ||
| 7 | import org.objectweb.asm.FieldVisitor; | ||
| 8 | import org.objectweb.asm.MethodVisitor; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 5 | import cuchaz.enigma.translation.representation.Signature; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 10 | import org.objectweb.asm.ClassVisitor; | ||
| 11 | import org.objectweb.asm.Handle; | ||
| 12 | import org.objectweb.asm.MethodVisitor; | ||
| 13 | import org.objectweb.asm.Opcodes; | ||
| 14 | |||
| 15 | public 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 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | |||
| 21 | import java.util.Collection; | ||
| 22 | import java.util.LinkedList; | ||
| 23 | import java.util.Set; | ||
| 24 | |||
| 25 | public 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 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import cuchaz.enigma.analysis.ParsedJar; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 19 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 21 | import org.objectweb.asm.ClassReader; | ||
| 22 | import org.objectweb.asm.Opcodes; | ||
| 23 | |||
| 24 | import java.util.Arrays; | ||
| 25 | import java.util.Collection; | ||
| 26 | import java.util.function.Consumer; | ||
| 27 | |||
| 28 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 5 | |||
| 6 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.HashMultimap; | ||
| 4 | import com.google.common.collect.Multimap; | ||
| 5 | import cuchaz.enigma.analysis.EntryReference; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 9 | |||
| 10 | import java.util.Collection; | ||
| 11 | import java.util.Map; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | |||
| 5 | public interface RemappableIndex { | ||
| 6 | void remap(Translator translator); | ||
| 7 | |||
| 8 | RemappableIndex remapped(Translator translator); | ||
| 9 | } | ||