diff options
| author | 2019-01-24 14:48:32 +0200 | |
|---|---|---|
| committer | 2019-01-24 13:48:32 +0100 | |
| commit | 00fcd0550fcdda621c2e4662f6ddd55ce673b931 (patch) | |
| tree | 6f9e4c24dbcc6d118fceec56adf7bf9d747a485c /src/main/java/cuchaz/enigma/analysis | |
| parent | mark as 0.13.0-SNAPSHOT for preliminary development (diff) | |
| download | enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.gz enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.xz enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.zip | |
[WIP] Mapping rework (#91)
* Move packages
* Mapping & entry refactor: first pass
* Fix deobf -> obf tree remapping
* Resolve various issues
* Give all entries the potential for parents and treat inner classes as children
* Deobf UI tree elements
* Tests pass
* Sort mapping output
* Fix delta tracking
* Index separation and first pass for #97
* Keep track of remapped jar index
* Fix child entries not being remapped
* Drop non-root entries
* Track dropped mappings
* Fix enigma mapping ordering
* EntryTreeNode interface
* Small tweaks
* Naive full index remap on rename
* Entries can resolve to more than one root entry
* Support alternative resolution strategies
* Bridge method resolution
* Tests pass
* Fix mappings being used where there are none
* Fix methods with different descriptors being considered unique. closes #89
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
30 files changed, 963 insertions, 1375 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java index 8181418..82ca669 100644 --- a/src/main/java/cuchaz/enigma/analysis/Access.java +++ b/src/main/java/cuchaz/enigma/analysis/Access.java | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.bytecode.AccessFlags; | 14 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 15 | 15 | ||
| 16 | import java.lang.reflect.Modifier; | 16 | import java.lang.reflect.Modifier; |
| 17 | 17 | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index e876bb0..0fc44ca 100644 --- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java | |||
| @@ -12,26 +12,28 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.analysis.index.JarIndex; |
| 17 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.translation.Translator; |
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 18 | 20 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | 21 | import javax.swing.tree.DefaultMutableTreeNode; |
| 22 | import java.util.Collection; | ||
| 20 | import java.util.List; | 23 | import java.util.List; |
| 21 | 24 | ||
| 22 | public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { | 25 | public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { |
| 23 | 26 | private final Translator translator; | |
| 24 | private final Translator deobfuscatingTranslator; | ||
| 25 | private final ClassEntry entry; | 27 | private final ClassEntry entry; |
| 26 | 28 | ||
| 27 | public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { | 29 | public ClassImplementationsTreeNode(Translator translator, ClassEntry entry) { |
| 28 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 30 | this.translator = translator; |
| 29 | this.entry = entry; | 31 | this.entry = entry; |
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { | 34 | public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { |
| 33 | // is this the node? | 35 | // is this the node? |
| 34 | if (node.entry.equals(entry.getOwnerClassEntry())) { | 36 | if (node.entry.equals(entry.getParent())) { |
| 35 | return node; | 37 | return node; |
| 36 | } | 38 | } |
| 37 | 39 | ||
| @@ -49,24 +51,19 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { | |||
| 49 | return this.entry; | 51 | return this.entry; |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | public String getDeobfClassName() { | ||
| 53 | return this.deobfuscatingTranslator.getTranslatedClass(entry).getClassName(); | ||
| 54 | } | ||
| 55 | |||
| 56 | @Override | 54 | @Override |
| 57 | public String toString() { | 55 | public String toString() { |
| 58 | String className = getDeobfClassName(); | 56 | return translator.translate(entry).toString(); |
| 59 | if (className == null) { | ||
| 60 | className = this.entry.getClassName(); | ||
| 61 | } | ||
| 62 | return className; | ||
| 63 | } | 57 | } |
| 64 | 58 | ||
| 65 | public void load(JarIndex index) { | 59 | public void load(JarIndex index) { |
| 66 | // get all method implementations | 60 | // get all method implementations |
| 67 | List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); | 61 | List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); |
| 68 | for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { | 62 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); |
| 69 | nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName))); | 63 | |
| 64 | Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry); | ||
| 65 | for (ClassEntry inheritor : inheritors) { | ||
| 66 | nodes.add(new ClassImplementationsTreeNode(translator, inheritor)); | ||
| 70 | } | 67 | } |
| 71 | 68 | ||
| 72 | // add them to this node | 69 | // add them to this node |
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index b8ee17d..7904c5f 100644 --- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java | |||
| @@ -12,25 +12,25 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 16 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 17 | 18 | ||
| 18 | import javax.swing.tree.DefaultMutableTreeNode; | 19 | import javax.swing.tree.DefaultMutableTreeNode; |
| 19 | import java.util.List; | 20 | import java.util.List; |
| 20 | 21 | ||
| 21 | public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { | 22 | public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { |
| 22 | 23 | private final Translator translator; | |
| 23 | private final Translator deobfuscatingTranslator; | ||
| 24 | private final ClassEntry obfClassEntry; | 24 | private final ClassEntry obfClassEntry; |
| 25 | 25 | ||
| 26 | public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { | 26 | public ClassInheritanceTreeNode(Translator translator, String obfClassName) { |
| 27 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 27 | this.translator = translator; |
| 28 | this.obfClassEntry = new ClassEntry(obfClassName); | 28 | this.obfClassEntry = new ClassEntry(obfClassName); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { | 31 | public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { |
| 32 | // is this the node? | 32 | // is this the node? |
| 33 | if (node.getObfClassName().equals(entry.getName())) { | 33 | if (node.getObfClassName().equals(entry.getFullName())) { |
| 34 | return node; | 34 | return node; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| @@ -45,27 +45,19 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { | |||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | public String getObfClassName() { | 47 | public String getObfClassName() { |
| 48 | return this.obfClassEntry.getClassName(); | 48 | return this.obfClassEntry.getFullName(); |
| 49 | } | ||
| 50 | |||
| 51 | public String getDeobfClassName() { | ||
| 52 | return this.deobfuscatingTranslator.getTranslatedClass(this.obfClassEntry).getClassName(); | ||
| 53 | } | 49 | } |
| 54 | 50 | ||
| 55 | @Override | 51 | @Override |
| 56 | public String toString() { | 52 | public String toString() { |
| 57 | String deobfClassName = getDeobfClassName(); | 53 | return translator.translate(obfClassEntry).getFullName(); |
| 58 | if (deobfClassName != null) { | ||
| 59 | return deobfClassName; | ||
| 60 | } | ||
| 61 | return this.obfClassEntry.getName(); | ||
| 62 | } | 54 | } |
| 63 | 55 | ||
| 64 | public void load(TranslationIndex ancestries, boolean recurse) { | 56 | public void load(InheritanceIndex ancestries, boolean recurse) { |
| 65 | // get all the child nodes | 57 | // get all the child nodes |
| 66 | List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); | 58 | List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); |
| 67 | for (ClassEntry subclassEntry : ancestries.getSubclass(this.obfClassEntry)) { | 59 | for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) { |
| 68 | nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); | 60 | nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName())); |
| 69 | } | 61 | } |
| 70 | 62 | ||
| 71 | // add them to this node | 63 | // add them to this node |
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java index ff5f2e9..90d8a6c 100644 --- a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java | |||
| @@ -12,12 +12,12 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.analysis.index.ReferenceIndex; |
| 17 | import cuchaz.enigma.mapping.entry.ClassEntry; | 17 | import cuchaz.enigma.translation.Translator; |
| 18 | import cuchaz.enigma.mapping.entry.Entry; | 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 19 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | 19 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 20 | import cuchaz.enigma.mapping.entry.MethodEntry; | 20 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; |
| 21 | 21 | ||
| 22 | import javax.swing.tree.DefaultMutableTreeNode; | 22 | import javax.swing.tree.DefaultMutableTreeNode; |
| 23 | import javax.swing.tree.TreeNode; | 23 | import javax.swing.tree.TreeNode; |
| @@ -29,7 +29,6 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode | |||
| 29 | private Translator deobfuscatingTranslator; | 29 | private Translator deobfuscatingTranslator; |
| 30 | private ClassEntry entry; | 30 | private ClassEntry entry; |
| 31 | private EntryReference<ClassEntry, MethodDefEntry> reference; | 31 | private EntryReference<ClassEntry, MethodDefEntry> reference; |
| 32 | private AccessFlags access; | ||
| 33 | 32 | ||
| 34 | public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { | 33 | public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { |
| 35 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 34 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| @@ -37,12 +36,10 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode | |||
| 37 | this.reference = null; | 36 | this.reference = null; |
| 38 | } | 37 | } |
| 39 | 38 | ||
| 40 | public ClassReferenceTreeNode(Translator deobfuscatingTranslator, | 39 | public ClassReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<ClassEntry, MethodDefEntry> reference) { |
| 41 | EntryReference<ClassEntry, MethodDefEntry> reference, AccessFlags access) { | ||
| 42 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 40 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 43 | this.entry = reference.entry; | 41 | this.entry = reference.entry; |
| 44 | this.reference = reference; | 42 | this.reference = reference; |
| 45 | this.access = access; | ||
| 46 | } | 43 | } |
| 47 | 44 | ||
| 48 | @Override | 45 | @Override |
| @@ -58,16 +55,17 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode | |||
| 58 | @Override | 55 | @Override |
| 59 | public String toString() { | 56 | public String toString() { |
| 60 | if (this.reference != null) { | 57 | if (this.reference != null) { |
| 61 | return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), | 58 | return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context)); |
| 62 | this.access); | ||
| 63 | } | 59 | } |
| 64 | return this.deobfuscatingTranslator.getTranslatedClass(this.entry).getName(); | 60 | return this.deobfuscatingTranslator.translate(this.entry).getFullName(); |
| 65 | } | 61 | } |
| 66 | 62 | ||
| 67 | public void load(JarIndex index, boolean recurse) { | 63 | public void load(JarIndex index, boolean recurse) { |
| 64 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 65 | |||
| 68 | // get all the child nodes | 66 | // get all the child nodes |
| 69 | for (EntryReference<ClassEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry)) { | 67 | for (EntryReference<ClassEntry, MethodDefEntry> reference : referenceIndex.getReferencesToClass(this.entry)) { |
| 70 | add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); | 68 | add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference)); |
| 71 | } | 69 | } |
| 72 | 70 | ||
| 73 | if (recurse && this.children != null) { | 71 | if (recurse && this.children != null) { |
| @@ -76,7 +74,7 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode | |||
| 76 | ClassReferenceTreeNode node = (ClassReferenceTreeNode) child; | 74 | ClassReferenceTreeNode node = (ClassReferenceTreeNode) child; |
| 77 | 75 | ||
| 78 | // don't recurse into ancestor | 76 | // don't recurse into ancestor |
| 79 | Set<Entry> ancestors = Sets.newHashSet(); | 77 | Set<Entry<?>> ancestors = Sets.newHashSet(); |
| 80 | TreeNode n = node; | 78 | TreeNode n = node; |
| 81 | while (n.getParent() != null) { | 79 | while (n.getParent() != null) { |
| 82 | n = n.getParent(); | 80 | n = n.getParent(); |
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java index df36c23..e122210 100644 --- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java +++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java | |||
| @@ -11,15 +11,20 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | 14 | import cuchaz.enigma.translation.Translatable; |
| 15 | import cuchaz.enigma.mapping.entry.Entry; | 15 | import cuchaz.enigma.translation.Translator; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 17 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 17 | import cuchaz.enigma.utils.Utils; | 22 | import cuchaz.enigma.utils.Utils; |
| 18 | 23 | ||
| 19 | import java.util.Arrays; | 24 | import java.util.Arrays; |
| 20 | import java.util.List; | 25 | import java.util.List; |
| 21 | 26 | ||
| 22 | public class EntryReference<E extends Entry, C extends Entry> { | 27 | public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements Translatable { |
| 23 | 28 | ||
| 24 | private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); | 29 | private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); |
| 25 | public E entry; | 30 | public E entry; |
| @@ -53,32 +58,24 @@ public class EntryReference<E extends Entry, C extends Entry> { | |||
| 53 | 58 | ||
| 54 | public ClassEntry getLocationClassEntry() { | 59 | public ClassEntry getLocationClassEntry() { |
| 55 | if (context != null) { | 60 | if (context != null) { |
| 56 | return context.getOwnerClassEntry(); | 61 | return context.getContainingClass(); |
| 57 | } | 62 | } |
| 58 | return entry.getOwnerClassEntry(); | 63 | return entry.getContainingClass(); |
| 59 | } | 64 | } |
| 60 | 65 | ||
| 61 | public boolean isNamed() { | 66 | public boolean isNamed() { |
| 62 | return this.sourceName; | 67 | return this.sourceName; |
| 63 | } | 68 | } |
| 64 | 69 | ||
| 65 | public Entry getNameableEntry() { | 70 | public Entry<?> getNameableEntry() { |
| 66 | if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) { | 71 | if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) { |
| 67 | // renaming a constructor really means renaming the class | 72 | // renaming a constructor really means renaming the class |
| 68 | return entry.getOwnerClassEntry(); | 73 | return entry.getContainingClass(); |
| 69 | } | 74 | } |
| 70 | return entry; | 75 | return entry; |
| 71 | } | 76 | } |
| 72 | 77 | ||
| 73 | public String getNameableName() { | 78 | public String getNameableName() { |
| 74 | if (getNameableEntry() instanceof ClassEntry) { | ||
| 75 | ClassEntry classEntry = (ClassEntry) getNameableEntry(); | ||
| 76 | if (classEntry.isInnerClass()) { | ||
| 77 | // make sure we only rename the inner class name | ||
| 78 | return classEntry.getInnermostClassName(); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | return getNameableEntry().getName(); | 79 | return getNameableEntry().getName(); |
| 83 | } | 80 | } |
| 84 | 81 | ||
| @@ -121,4 +118,9 @@ public class EntryReference<E extends Entry, C extends Entry> { | |||
| 121 | } | 118 | } |
| 122 | return buf.toString(); | 119 | return buf.toString(); |
| 123 | } | 120 | } |
| 121 | |||
| 122 | @Override | ||
| 123 | public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { | ||
| 124 | return new EntryReference<>(translator.translate(entry), translator.translate(context), this); | ||
| 125 | } | ||
| 124 | } | 126 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java deleted file mode 100644 index c474d68..0000000 --- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java +++ /dev/null | |||
| @@ -1,167 +0,0 @@ | |||
| 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; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.mapping.*; | ||
| 18 | import cuchaz.enigma.mapping.entry.*; | ||
| 19 | |||
| 20 | import java.util.AbstractMap; | ||
| 21 | import java.util.List; | ||
| 22 | import java.util.Map; | ||
| 23 | import java.util.Set; | ||
| 24 | |||
| 25 | public class EntryRenamer { | ||
| 26 | |||
| 27 | public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) { | ||
| 28 | List<T> entries = Lists.newArrayList(); | ||
| 29 | for (T val : set) { | ||
| 30 | entries.add(renameClassesInThing(renames, val)); | ||
| 31 | } | ||
| 32 | set.clear(); | ||
| 33 | set.addAll(entries); | ||
| 34 | } | ||
| 35 | |||
| 36 | public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) { | ||
| 37 | // for each key/value pair... | ||
| 38 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | ||
| 39 | for (Map.Entry<Key, Val> entry : map.entrySet()) { | ||
| 40 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); | ||
| 41 | } | ||
| 42 | map.clear(); | ||
| 43 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | ||
| 44 | map.put(entry.getKey(), entry.getValue()); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) { | ||
| 49 | // for each key/value pair... | ||
| 50 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | ||
| 51 | for (Map.Entry<Key, Val> entry : map.entries()) { | ||
| 52 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); | ||
| 53 | } | ||
| 54 | map.clear(); | ||
| 55 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | ||
| 56 | map.put(entry.getKey(), entry.getValue()); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) { | ||
| 61 | // for each key/value pair... | ||
| 62 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | ||
| 63 | for (Map.Entry<Key, Val> entry : map.entries()) { | ||
| 64 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); | ||
| 65 | } | ||
| 66 | map.clear(); | ||
| 67 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | ||
| 68 | map.put(entry.getKey(), entry.getValue()); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) { | ||
| 73 | // for each key/value pair... | ||
| 74 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | ||
| 75 | for (Map.Entry<Key, Val> entry : map.entrySet()) { | ||
| 76 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); | ||
| 77 | } | ||
| 78 | map.clear(); | ||
| 79 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | ||
| 80 | map.put(entry.getKey(), entry.getValue()); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | @SuppressWarnings("unchecked") | ||
| 85 | public static <T> T renameMethodsInThing(Map<MethodEntry, MethodEntry> renames, T thing) { | ||
| 86 | if (thing instanceof MethodEntry) { | ||
| 87 | MethodEntry methodEntry = (MethodEntry) thing; | ||
| 88 | MethodEntry newMethodEntry = renames.get(methodEntry); | ||
| 89 | if (newMethodEntry != null) { | ||
| 90 | return (T) new MethodEntry( | ||
| 91 | methodEntry.getOwnerClassEntry(), | ||
| 92 | newMethodEntry.getName(), | ||
| 93 | methodEntry.getDesc() | ||
| 94 | ); | ||
| 95 | } | ||
| 96 | return thing; | ||
| 97 | } else if (thing instanceof LocalVariableEntry) { | ||
| 98 | LocalVariableEntry variableEntry = (LocalVariableEntry) thing; | ||
| 99 | return (T) new LocalVariableEntry( | ||
| 100 | renameMethodsInThing(renames, variableEntry.getOwnerEntry()), | ||
| 101 | variableEntry.getIndex(), | ||
| 102 | variableEntry.getName(), | ||
| 103 | variableEntry.isParameter() | ||
| 104 | ); | ||
| 105 | } else if (thing instanceof EntryReference) { | ||
| 106 | EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; | ||
| 107 | reference.entry = renameMethodsInThing(renames, reference.entry); | ||
| 108 | reference.context = renameMethodsInThing(renames, reference.context); | ||
| 109 | return thing; | ||
| 110 | } | ||
| 111 | return thing; | ||
| 112 | } | ||
| 113 | |||
| 114 | @SuppressWarnings("unchecked") | ||
| 115 | public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) { | ||
| 116 | if (thing instanceof String) { | ||
| 117 | String stringEntry = (String) thing; | ||
| 118 | if (renames.containsKey(stringEntry)) { | ||
| 119 | return (T) renames.get(stringEntry); | ||
| 120 | } | ||
| 121 | } else if (thing instanceof ClassEntry) { | ||
| 122 | ClassEntry classEntry = (ClassEntry) thing; | ||
| 123 | return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); | ||
| 124 | } else if (thing instanceof FieldDefEntry) { | ||
| 125 | FieldDefEntry fieldEntry = (FieldDefEntry) thing; | ||
| 126 | return (T) new FieldDefEntry( | ||
| 127 | renameClassesInThing(renames, fieldEntry.getOwnerClassEntry()), | ||
| 128 | fieldEntry.getName(), | ||
| 129 | renameClassesInThing(renames, fieldEntry.getDesc()), | ||
| 130 | renameClassesInThing(renames, fieldEntry.getSignature()), | ||
| 131 | fieldEntry.getAccess() | ||
| 132 | ); | ||
| 133 | } else if (thing instanceof MethodDefEntry) { | ||
| 134 | MethodDefEntry methodEntry = (MethodDefEntry) thing; | ||
| 135 | return (T) new MethodDefEntry( | ||
| 136 | renameClassesInThing(renames, methodEntry.getOwnerClassEntry()), | ||
| 137 | methodEntry.getName(), | ||
| 138 | renameClassesInThing(renames, methodEntry.getDesc()), | ||
| 139 | renameClassesInThing(renames, methodEntry.getSignature()), | ||
| 140 | methodEntry.getAccess() | ||
| 141 | ); | ||
| 142 | } else if (thing instanceof MethodEntry) { | ||
| 143 | MethodEntry methodEntry = (MethodEntry) thing; | ||
| 144 | return (T) new MethodEntry( | ||
| 145 | renameClassesInThing(renames, methodEntry.getOwnerClassEntry()), | ||
| 146 | methodEntry.getName(), | ||
| 147 | renameClassesInThing(renames, methodEntry.getDesc()) | ||
| 148 | ); | ||
| 149 | } else if (thing instanceof LocalVariableEntry) { | ||
| 150 | LocalVariableEntry argumentEntry = (LocalVariableEntry) thing; | ||
| 151 | return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName(), argumentEntry.isParameter()); | ||
| 152 | } else if (thing instanceof EntryReference) { | ||
| 153 | EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; | ||
| 154 | reference.entry = renameClassesInThing(renames, reference.entry); | ||
| 155 | reference.context = renameClassesInThing(renames, reference.context); | ||
| 156 | return thing; | ||
| 157 | } else if (thing instanceof MethodDescriptor) { | ||
| 158 | return (T) ((MethodDescriptor) thing).remap(className -> renameClassesInThing(renames, className)); | ||
| 159 | } else if (thing instanceof TypeDescriptor) { | ||
| 160 | return (T) ((TypeDescriptor) thing).remap(className -> renameClassesInThing(renames, className)); | ||
| 161 | } else if (thing instanceof Signature) { | ||
| 162 | return (T) ((Signature) thing).remap(className -> renameClassesInThing(renames, className)); | ||
| 163 | } | ||
| 164 | |||
| 165 | return thing; | ||
| 166 | } | ||
| 167 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 2318a2b..4beab7f 100644 --- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java | |||
| @@ -11,32 +11,31 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.bytecode.AccessFlags; | 14 | import cuchaz.enigma.analysis.index.JarIndex; |
| 15 | import cuchaz.enigma.mapping.*; | 15 | import cuchaz.enigma.analysis.index.ReferenceIndex; |
| 16 | import cuchaz.enigma.mapping.entry.FieldEntry; | 16 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | 17 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 18 | import cuchaz.enigma.mapping.entry.MethodEntry; | 18 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; |
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 19 | 20 | ||
| 20 | import javax.swing.tree.DefaultMutableTreeNode; | 21 | import javax.swing.tree.DefaultMutableTreeNode; |
| 21 | 22 | ||
| 22 | public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> { | 23 | public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> { |
| 23 | 24 | ||
| 24 | private Translator deobfuscatingTranslator; | 25 | private final Translator translator; |
| 25 | private FieldEntry entry; | 26 | private FieldEntry entry; |
| 26 | private EntryReference<FieldEntry, MethodDefEntry> reference; | 27 | private EntryReference<FieldEntry, MethodDefEntry> reference; |
| 27 | private AccessFlags access; | ||
| 28 | 28 | ||
| 29 | public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { | 29 | public FieldReferenceTreeNode(Translator translator, FieldEntry entry) { |
| 30 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 30 | this.translator = translator; |
| 31 | this.entry = entry; | 31 | this.entry = entry; |
| 32 | this.reference = null; | 32 | this.reference = null; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, MethodDefEntry> reference, AccessFlags access) { | 35 | private FieldReferenceTreeNode(Translator translator, EntryReference<FieldEntry, MethodDefEntry> reference) { |
| 36 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 36 | this.translator = translator; |
| 37 | this.entry = reference.entry; | 37 | this.entry = reference.entry; |
| 38 | this.reference = reference; | 38 | this.reference = reference; |
| 39 | this.access = access; | ||
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | @Override | 41 | @Override |
| @@ -52,27 +51,29 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re | |||
| 52 | @Override | 51 | @Override |
| 53 | public String toString() { | 52 | public String toString() { |
| 54 | if (this.reference != null) { | 53 | if (this.reference != null) { |
| 55 | return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), this.access); | 54 | return String.format("%s", translator.translate(this.reference.context)); |
| 56 | } | 55 | } |
| 57 | return deobfuscatingTranslator.getTranslatedField(entry).getName(); | 56 | return translator.translate(entry).toString(); |
| 58 | } | 57 | } |
| 59 | 58 | ||
| 60 | public void load(JarIndex index, boolean recurse) { | 59 | public void load(JarIndex index, boolean recurse) { |
| 60 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 61 | |||
| 61 | // get all the child nodes | 62 | // get all the child nodes |
| 62 | if (this.reference == null) { | 63 | if (this.reference == null) { |
| 63 | for (EntryReference<FieldEntry, MethodDefEntry> reference : index.getFieldReferences(this.entry)) { | 64 | for (EntryReference<FieldEntry, MethodDefEntry> reference : referenceIndex.getReferencesToField(this.entry)) { |
| 64 | add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); | 65 | add(new FieldReferenceTreeNode(translator, reference)); |
| 65 | } | 66 | } |
| 66 | } else { | 67 | } else { |
| 67 | for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.reference.context)) { | 68 | for (EntryReference<MethodEntry, MethodDefEntry> reference : referenceIndex.getReferencesToMethod(this.reference.context)) { |
| 68 | add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.reference.context))); | 69 | add(new MethodReferenceTreeNode(translator, reference)); |
| 69 | } | 70 | } |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | if (recurse && children != null) { | 73 | if (recurse && children != null) { |
| 73 | for (Object node : children) { | 74 | for (Object node : children) { |
| 74 | if (node instanceof MethodReferenceTreeNode) { | 75 | if (node instanceof MethodReferenceTreeNode) { |
| 75 | ((MethodReferenceTreeNode) node).load(index, true); | 76 | ((MethodReferenceTreeNode) node).load(index, true, false); |
| 76 | } else if (node instanceof FieldReferenceTreeNode) { | 77 | } else if (node instanceof FieldReferenceTreeNode) { |
| 77 | ((FieldReferenceTreeNode) node).load(index, true); | 78 | ((FieldReferenceTreeNode) node).load(index, true); |
| 78 | } | 79 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java deleted file mode 100644 index b6ab2d5..0000000 --- a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java +++ /dev/null | |||
| @@ -1,29 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 4 | import org.objectweb.asm.ClassVisitor; | ||
| 5 | |||
| 6 | public class IndexInnerClassVisitor extends ClassVisitor { | ||
| 7 | private final JarIndex index; | ||
| 8 | |||
| 9 | public IndexInnerClassVisitor(JarIndex index, int api) { | ||
| 10 | super(api); | ||
| 11 | this.index = index; | ||
| 12 | } | ||
| 13 | |||
| 14 | public IndexInnerClassVisitor(JarIndex index, int api, ClassVisitor cv) { | ||
| 15 | super(api, cv); | ||
| 16 | this.index = index; | ||
| 17 | } | ||
| 18 | |||
| 19 | @Override | ||
| 20 | public void visitInnerClass(String name, String outerName, String innerName, int access) { | ||
| 21 | ClassEntry entry = new ClassEntry(name); | ||
| 22 | // Ignore anonymous classes | ||
| 23 | if (innerName != null && outerName != null) { | ||
| 24 | ClassEntry outerEntry = new ClassEntry(outerName); | ||
| 25 | index.indexInnerClass(entry, outerEntry); | ||
| 26 | } | ||
| 27 | super.visitInnerClass(name, outerName, innerName, access); | ||
| 28 | } | ||
| 29 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java deleted file mode 100644 index f37f1e9..0000000 --- a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java +++ /dev/null | |||
| @@ -1,77 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 4 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 5 | import cuchaz.enigma.mapping.Signature; | ||
| 6 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 7 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | ||
| 8 | import org.objectweb.asm.ClassVisitor; | ||
| 9 | import org.objectweb.asm.Handle; | ||
| 10 | import org.objectweb.asm.MethodVisitor; | ||
| 11 | import org.objectweb.asm.Opcodes; | ||
| 12 | |||
| 13 | public class IndexReferenceVisitor extends ClassVisitor { | ||
| 14 | private final JarIndex index; | ||
| 15 | private ClassEntry classEntry; | ||
| 16 | |||
| 17 | public IndexReferenceVisitor(JarIndex index, int api) { | ||
| 18 | super(api); | ||
| 19 | this.index = index; | ||
| 20 | } | ||
| 21 | |||
| 22 | @Override | ||
| 23 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 24 | this.classEntry = new ClassEntry(name); | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | ||
| 29 | MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); | ||
| 30 | return new Method(this.index, entry, this.api); | ||
| 31 | } | ||
| 32 | |||
| 33 | private class Method extends MethodVisitor { | ||
| 34 | private final JarIndex index; | ||
| 35 | private final MethodDefEntry callerEntry; | ||
| 36 | |||
| 37 | public Method(JarIndex index, MethodDefEntry callerEntry, int api) { | ||
| 38 | super(api); | ||
| 39 | this.index = index; | ||
| 40 | this.callerEntry = callerEntry; | ||
| 41 | } | ||
| 42 | |||
| 43 | @Override | ||
| 44 | public void visitFieldInsn(int opcode, String owner, String name, String desc) { | ||
| 45 | this.index.indexFieldAccess(callerEntry, owner, name, desc); | ||
| 46 | } | ||
| 47 | |||
| 48 | @Override | ||
| 49 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { | ||
| 50 | this.index.indexMethodCall(callerEntry, owner, name, desc); | ||
| 51 | } | ||
| 52 | |||
| 53 | @Override | ||
| 54 | public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { | ||
| 55 | for (Object bsmArg : bsmArgs){ | ||
| 56 | if (bsmArg instanceof Handle){ | ||
| 57 | Handle handle = (Handle)bsmArg; | ||
| 58 | switch (handle.getTag()){ | ||
| 59 | case Opcodes.H_GETFIELD: | ||
| 60 | case Opcodes.H_GETSTATIC: | ||
| 61 | case Opcodes.H_PUTFIELD: | ||
| 62 | case Opcodes.H_PUTSTATIC: | ||
| 63 | this.index.indexFieldAccess(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc()); | ||
| 64 | break; | ||
| 65 | case Opcodes.H_INVOKEINTERFACE: | ||
| 66 | case Opcodes.H_INVOKESPECIAL: | ||
| 67 | case Opcodes.H_INVOKESTATIC: | ||
| 68 | case Opcodes.H_INVOKEVIRTUAL: | ||
| 69 | case Opcodes.H_NEWINVOKESPECIAL: | ||
| 70 | this.index.indexMethodCall(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc()); | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java b/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java new file mode 100644 index 0000000..4ca7cd1 --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.google.common.collect.Lists; | ||
| 4 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 5 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 6 | import cuchaz.enigma.translation.Translator; | ||
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 10 | |||
| 11 | import java.util.List; | ||
| 12 | |||
| 13 | public class IndexTreeBuilder { | ||
| 14 | private final JarIndex index; | ||
| 15 | |||
| 16 | public IndexTreeBuilder(JarIndex index) { | ||
| 17 | this.index = index; | ||
| 18 | } | ||
| 19 | |||
| 20 | public ClassInheritanceTreeNode buildClassInheritance(Translator translator, ClassEntry obfClassEntry) { | ||
| 21 | // get the root node | ||
| 22 | List<String> ancestry = Lists.newArrayList(); | ||
| 23 | ancestry.add(obfClassEntry.getFullName()); | ||
| 24 | for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) { | ||
| 25 | ancestry.add(classEntry.getFullName()); | ||
| 26 | } | ||
| 27 | |||
| 28 | ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(translator, ancestry.get(ancestry.size() - 1)); | ||
| 29 | |||
| 30 | // expand all children recursively | ||
| 31 | rootNode.load(index.getInheritanceIndex(), true); | ||
| 32 | |||
| 33 | return rootNode; | ||
| 34 | } | ||
| 35 | |||
| 36 | public ClassImplementationsTreeNode buildClassImplementations(Translator translator, ClassEntry obfClassEntry) { | ||
| 37 | if (index.getInheritanceIndex().isParent(obfClassEntry)) { | ||
| 38 | ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(translator, obfClassEntry); | ||
| 39 | node.load(index); | ||
| 40 | return node; | ||
| 41 | } | ||
| 42 | return null; | ||
| 43 | } | ||
| 44 | |||
| 45 | public MethodInheritanceTreeNode buildMethodInheritance(Translator translator, MethodEntry obfMethodEntry) { | ||
| 46 | MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); | ||
| 47 | |||
| 48 | // make a root node at the base | ||
| 49 | MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( | ||
| 50 | translator, resolvedEntry, | ||
| 51 | index.getEntryIndex().hasMethod(resolvedEntry) | ||
| 52 | ); | ||
| 53 | |||
| 54 | // expand the full tree | ||
| 55 | rootNode.load(index, true); | ||
| 56 | |||
| 57 | return rootNode; | ||
| 58 | } | ||
| 59 | |||
| 60 | public List<MethodImplementationsTreeNode> buildMethodImplementations(Translator translator, MethodEntry obfMethodEntry) { | ||
| 61 | EntryIndex entryIndex = index.getEntryIndex(); | ||
| 62 | |||
| 63 | List<MethodEntry> ancestorMethodEntries = Lists.newArrayList(); | ||
| 64 | |||
| 65 | if (entryIndex.hasMethod(obfMethodEntry)) { | ||
| 66 | ancestorMethodEntries.add(obfMethodEntry); | ||
| 67 | } | ||
| 68 | |||
| 69 | for (ClassEntry ancestorEntry : index.getInheritanceIndex().getAncestors(obfMethodEntry.getParent())) { | ||
| 70 | MethodEntry ancestorMethod = obfMethodEntry.withParent(ancestorEntry); | ||
| 71 | if (entryIndex.hasMethod(ancestorMethod)) { | ||
| 72 | ancestorMethodEntries.add(ancestorMethod); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); | ||
| 77 | if (!ancestorMethodEntries.isEmpty()) { | ||
| 78 | for (MethodEntry interfaceMethodEntry : ancestorMethodEntries) { | ||
| 79 | MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, interfaceMethodEntry); | ||
| 80 | node.load(index); | ||
| 81 | nodes.add(node); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | return nodes; | ||
| 86 | } | ||
| 87 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java deleted file mode 100644 index 361c8e7..0000000 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ /dev/null | |||
| @@ -1,583 +0,0 @@ | |||
| 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; | ||
| 13 | |||
| 14 | import com.google.common.collect.*; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.*; | ||
| 17 | import cuchaz.enigma.mapping.entry.*; | ||
| 18 | import org.objectweb.asm.ClassReader; | ||
| 19 | import org.objectweb.asm.ClassVisitor; | ||
| 20 | import org.objectweb.asm.Opcodes; | ||
| 21 | |||
| 22 | import java.util.*; | ||
| 23 | |||
| 24 | public class JarIndex { | ||
| 25 | |||
| 26 | private final ReferencedEntryPool entryPool; | ||
| 27 | |||
| 28 | private Set<ClassEntry> obfClassEntries; | ||
| 29 | private TranslationIndex translationIndex; | ||
| 30 | private Map<Entry, AccessFlags> access; | ||
| 31 | private Multimap<ClassEntry, FieldDefEntry> fields; | ||
| 32 | private Multimap<ClassEntry, MethodDefEntry> methods; | ||
| 33 | private Multimap<String, MethodDefEntry> methodImplementations; | ||
| 34 | private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodsReferencing; | ||
| 35 | private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> methodsReferencingClasses; | ||
| 36 | private Multimap<MethodEntry, MethodEntry> methodReferences; | ||
| 37 | private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences; | ||
| 38 | private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; | ||
| 39 | private Map<ClassEntry, ClassEntry> outerClassesByInner; | ||
| 40 | private Map<MethodEntry, MethodEntry> bridgedMethods; | ||
| 41 | private Set<MethodEntry> syntheticMethods; | ||
| 42 | |||
| 43 | public JarIndex(ReferencedEntryPool entryPool) { | ||
| 44 | this.entryPool = entryPool; | ||
| 45 | this.obfClassEntries = Sets.newHashSet(); | ||
| 46 | this.translationIndex = new TranslationIndex(entryPool); | ||
| 47 | this.access = Maps.newHashMap(); | ||
| 48 | this.fields = HashMultimap.create(); | ||
| 49 | this.methods = HashMultimap.create(); | ||
| 50 | this.methodImplementations = HashMultimap.create(); | ||
| 51 | this.methodsReferencingClasses = HashMultimap.create(); | ||
| 52 | this.methodsReferencing = HashMultimap.create(); | ||
| 53 | this.methodReferences = HashMultimap.create(); | ||
| 54 | this.fieldReferences = HashMultimap.create(); | ||
| 55 | this.innerClassesByOuter = HashMultimap.create(); | ||
| 56 | this.outerClassesByInner = Maps.newHashMap(); | ||
| 57 | this.bridgedMethods = Maps.newHashMap(); | ||
| 58 | this.syntheticMethods = Sets.newHashSet(); | ||
| 59 | } | ||
| 60 | |||
| 61 | public void indexJar(ParsedJar jar, boolean buildInnerClasses) { | ||
| 62 | |||
| 63 | // step 1: read the class names | ||
| 64 | obfClassEntries.addAll(jar.getClassEntries()); | ||
| 65 | |||
| 66 | // step 2: index classes, fields, methods, interfaces | ||
| 67 | if (buildInnerClasses) { | ||
| 68 | // + step 5: index inner classes | ||
| 69 | jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5, new IndexInnerClassVisitor(this, Opcodes.ASM5)), ClassReader.SKIP_CODE); | ||
| 70 | } else { | ||
| 71 | jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); | ||
| 72 | } | ||
| 73 | |||
| 74 | // step 3: index field, method, constructor references | ||
| 75 | jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES); | ||
| 76 | |||
| 77 | // step 4: index access and bridged methods | ||
| 78 | for (MethodDefEntry methodEntry : methods.values()) { | ||
| 79 | // look for access and bridged methods | ||
| 80 | MethodEntry accessedMethod = findAccessMethod(methodEntry); | ||
| 81 | if (accessedMethod != null) { | ||
| 82 | if (isBridgedMethod(accessedMethod, methodEntry)) { | ||
| 83 | this.bridgedMethods.put(methodEntry, accessedMethod); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | if (buildInnerClasses) { | ||
| 89 | // step 6: update other indices with inner class info | ||
| 90 | Map<String, String> renames = Maps.newHashMap(); | ||
| 91 | for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) { | ||
| 92 | String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); | ||
| 93 | if (!innerClassEntry.getName().equals(newName)) { | ||
| 94 | // DEBUG | ||
| 95 | //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); | ||
| 96 | renames.put(innerClassEntry.getName(), newName); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); | ||
| 100 | this.translationIndex.renameClasses(renames); | ||
| 101 | EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); | ||
| 102 | EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencingClasses); | ||
| 103 | EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencing); | ||
| 104 | EntryRenamer.renameClassesInMultimap(renames, this.methodReferences); | ||
| 105 | EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); | ||
| 106 | EntryRenamer.renameClassesInMap(renames, this.access); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) { | ||
| 111 | for (String interfaceName : interfaces) { | ||
| 112 | if (name.equals(interfaceName)) { | ||
| 113 | throw new IllegalArgumentException("Class cannot be its own interface! " + name); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | ClassDefEntry entry = this.translationIndex.indexClass(access, name, signature, superName, interfaces); | ||
| 117 | this.access.put(entry, entry.getAccess()); | ||
| 118 | return entry; | ||
| 119 | } | ||
| 120 | |||
| 121 | protected void indexField(ClassDefEntry owner, int access, String name, String desc, String signature) { | ||
| 122 | FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access)); | ||
| 123 | this.translationIndex.indexField(fieldEntry); | ||
| 124 | this.access.put(fieldEntry, fieldEntry.getAccess()); | ||
| 125 | this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry); | ||
| 126 | } | ||
| 127 | |||
| 128 | protected void indexMethod(ClassDefEntry owner, int access, String name, String desc, String signature) { | ||
| 129 | MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); | ||
| 130 | this.translationIndex.indexMethod(methodEntry); | ||
| 131 | this.access.put(methodEntry, methodEntry.getAccess()); | ||
| 132 | this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry); | ||
| 133 | |||
| 134 | if (new AccessFlags(access).isSynthetic()) { | ||
| 135 | syntheticMethods.add(methodEntry); | ||
| 136 | } | ||
| 137 | |||
| 138 | // we don't care about constructors here | ||
| 139 | if (!methodEntry.isConstructor()) { | ||
| 140 | // index implementation | ||
| 141 | this.methodImplementations.put(methodEntry.getClassName(), methodEntry); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) { | ||
| 146 | ClassEntry referencedClass = entryPool.getClass(owner); | ||
| 147 | MethodEntry referencedMethod = new MethodEntry(referencedClass, name, new MethodDescriptor(desc)); | ||
| 148 | ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod); | ||
| 149 | if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) { | ||
| 150 | referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry); | ||
| 151 | } | ||
| 152 | methodsReferencing.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry)); | ||
| 153 | if (referencedMethod.isConstructor()) { | ||
| 154 | methodsReferencingClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedMethod.getName(), callerEntry)); | ||
| 155 | } | ||
| 156 | methodReferences.put(callerEntry, referencedMethod); | ||
| 157 | } | ||
| 158 | |||
| 159 | protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) { | ||
| 160 | FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc)); | ||
| 161 | ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField); | ||
| 162 | if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) { | ||
| 163 | referencedField = referencedField.updateOwnership(resolvedClassEntry); | ||
| 164 | } | ||
| 165 | fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry)); | ||
| 166 | } | ||
| 167 | |||
| 168 | public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) { | ||
| 169 | this.innerClassesByOuter.put(outerEntry, innerEntry); | ||
| 170 | this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry); | ||
| 171 | } | ||
| 172 | |||
| 173 | private MethodEntry findAccessMethod(MethodDefEntry method) { | ||
| 174 | |||
| 175 | // we want to find all compiler-added methods that directly call another with no processing | ||
| 176 | |||
| 177 | // skip non-synthetic methods | ||
| 178 | if (!method.getAccess().isSynthetic()) { | ||
| 179 | return null; | ||
| 180 | } | ||
| 181 | |||
| 182 | // get all the methods that we call | ||
| 183 | final Collection<MethodEntry> referencedMethods = methodReferences.get(method); | ||
| 184 | |||
| 185 | // is there just one? | ||
| 186 | if (referencedMethods.size() != 1) { | ||
| 187 | return null; | ||
| 188 | } | ||
| 189 | |||
| 190 | return referencedMethods.stream().findFirst().orElse(null); | ||
| 191 | } | ||
| 192 | |||
| 193 | private boolean isBridgedMethod(MethodEntry called, MethodEntry access) { | ||
| 194 | // Bridged methods will always have the same name as the method they are calling | ||
| 195 | // They will also have the same amount of parameters (though equal descriptors cannot be guaranteed) | ||
| 196 | if (!called.getName().equals(access.getName()) || called.getDesc().getArgumentDescs().size() != access.getDesc().getArgumentDescs().size()) { | ||
| 197 | return false; | ||
| 198 | } | ||
| 199 | |||
| 200 | TypeDescriptor accessReturn = access.getDesc().getReturnDesc(); | ||
| 201 | TypeDescriptor calledReturn = called.getDesc().getReturnDesc(); | ||
| 202 | if (calledReturn.isVoid() || calledReturn.isPrimitive() || accessReturn.isVoid() || accessReturn.isPrimitive()) { | ||
| 203 | return false; | ||
| 204 | } | ||
| 205 | |||
| 206 | // Bridged methods will never have the same type as what they are calling | ||
| 207 | if (accessReturn.equals(calledReturn)) { | ||
| 208 | return false; | ||
| 209 | } | ||
| 210 | |||
| 211 | String accessType = accessReturn.toString(); | ||
| 212 | |||
| 213 | // If we're casting down from generic type to type-erased Object we're a bridge method | ||
| 214 | if (accessType.equals("Ljava/lang/Object;")) { | ||
| 215 | return true; | ||
| 216 | } | ||
| 217 | |||
| 218 | // Now we need to detect cases where we are being casted down to a higher type bound | ||
| 219 | List<ClassEntry> calledAncestry = translationIndex.getAncestry(calledReturn.getTypeEntry()); | ||
| 220 | return calledAncestry.contains(accessReturn.getTypeEntry()); | ||
| 221 | } | ||
| 222 | |||
| 223 | public Set<ClassEntry> getObfClassEntries() { | ||
| 224 | return this.obfClassEntries; | ||
| 225 | } | ||
| 226 | |||
| 227 | public Collection<FieldDefEntry> getObfFieldEntries() { | ||
| 228 | return this.fields.values(); | ||
| 229 | } | ||
| 230 | |||
| 231 | public Collection<FieldDefEntry> getObfFieldEntries(ClassEntry classEntry) { | ||
| 232 | return this.fields.get(classEntry); | ||
| 233 | } | ||
| 234 | |||
| 235 | public Collection<MethodDefEntry> getObfBehaviorEntries() { | ||
| 236 | return this.methods.values(); | ||
| 237 | } | ||
| 238 | |||
| 239 | public Collection<MethodDefEntry> getObfBehaviorEntries(ClassEntry classEntry) { | ||
| 240 | return this.methods.get(classEntry); | ||
| 241 | } | ||
| 242 | |||
| 243 | public TranslationIndex getTranslationIndex() { | ||
| 244 | return this.translationIndex; | ||
| 245 | } | ||
| 246 | |||
| 247 | @Deprecated | ||
| 248 | public Access getAccess(Entry entry) { | ||
| 249 | AccessFlags flags = getAccessFlags(entry); | ||
| 250 | return flags != null ? Access.get(flags) : null; | ||
| 251 | } | ||
| 252 | |||
| 253 | public AccessFlags getAccessFlags(Entry entry) { | ||
| 254 | return this.access.get(entry); | ||
| 255 | } | ||
| 256 | |||
| 257 | public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { | ||
| 258 | |||
| 259 | // get the root node | ||
| 260 | List<String> ancestry = Lists.newArrayList(); | ||
| 261 | ancestry.add(obfClassEntry.getName()); | ||
| 262 | for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) { | ||
| 263 | if (containsObfClass(classEntry)) { | ||
| 264 | ancestry.add(classEntry.getName()); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( | ||
| 268 | deobfuscatingTranslator, | ||
| 269 | ancestry.get(ancestry.size() - 1) | ||
| 270 | ); | ||
| 271 | |||
| 272 | // expand all children recursively | ||
| 273 | rootNode.load(this.translationIndex, true); | ||
| 274 | |||
| 275 | return rootNode; | ||
| 276 | } | ||
| 277 | |||
| 278 | public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { | ||
| 279 | |||
| 280 | // is this even an interface? | ||
| 281 | if (isInterface(obfClassEntry.getClassName())) { | ||
| 282 | ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); | ||
| 283 | node.load(this); | ||
| 284 | return node; | ||
| 285 | } | ||
| 286 | return null; | ||
| 287 | } | ||
| 288 | |||
| 289 | public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { | ||
| 290 | // travel to the ancestor implementation | ||
| 291 | LinkedList<ClassEntry> entries = new LinkedList<>(); | ||
| 292 | entries.add(obfMethodEntry.getOwnerClassEntry()); | ||
| 293 | |||
| 294 | // TODO: This could be optimized to not go through interfaces repeatedly... | ||
| 295 | |||
| 296 | ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry(); | ||
| 297 | |||
| 298 | for (ClassEntry itf : getInterfaces(obfMethodEntry.getOwnerClassEntry().getClassName())) { | ||
| 299 | MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); | ||
| 300 | if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) { | ||
| 301 | baseImplementationClassEntry = itf; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(entries.remove())) { | ||
| 306 | MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); | ||
| 307 | if (ancestorMethodEntry != null) { | ||
| 308 | if (containsObfMethod(ancestorMethodEntry)) { | ||
| 309 | baseImplementationClassEntry = ancestorClassEntry; | ||
| 310 | } | ||
| 311 | |||
| 312 | for (ClassEntry itf : getInterfaces(ancestorClassEntry.getClassName())) { | ||
| 313 | MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); | ||
| 314 | if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) { | ||
| 315 | baseImplementationClassEntry = itf; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | // make a root node at the base | ||
| 322 | MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); | ||
| 323 | MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( | ||
| 324 | deobfuscatingTranslator, | ||
| 325 | methodEntry, | ||
| 326 | containsObfMethod(methodEntry) | ||
| 327 | ); | ||
| 328 | |||
| 329 | // expand the full tree | ||
| 330 | rootNode.load(this, true); | ||
| 331 | |||
| 332 | return rootNode; | ||
| 333 | } | ||
| 334 | |||
| 335 | public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { | ||
| 336 | |||
| 337 | List<MethodEntry> interfaceMethodEntries = Lists.newArrayList(); | ||
| 338 | |||
| 339 | // is this method on an interface? | ||
| 340 | if (isInterface(obfMethodEntry.getClassName())) { | ||
| 341 | interfaceMethodEntries.add(obfMethodEntry); | ||
| 342 | } else { | ||
| 343 | // get the interface class | ||
| 344 | for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { | ||
| 345 | |||
| 346 | // is this method defined in this interface? | ||
| 347 | MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); | ||
| 348 | if (methodInterface != null && containsObfMethod(methodInterface)) { | ||
| 349 | interfaceMethodEntries.add(methodInterface); | ||
| 350 | } | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); | ||
| 355 | if (!interfaceMethodEntries.isEmpty()) { | ||
| 356 | for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { | ||
| 357 | MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); | ||
| 358 | node.load(this); | ||
| 359 | nodes.add(node); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | return nodes; | ||
| 363 | } | ||
| 364 | |||
| 365 | public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { | ||
| 366 | AccessFlags flags = getAccessFlags(obfMethodEntry); | ||
| 367 | if (flags.isPrivate() || flags.isStatic()) { | ||
| 368 | return Collections.singleton(obfMethodEntry); | ||
| 369 | } | ||
| 370 | |||
| 371 | Set<MethodEntry> methodEntries = Sets.newHashSet(); | ||
| 372 | getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry)); | ||
| 373 | return methodEntries; | ||
| 374 | } | ||
| 375 | |||
| 376 | private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { | ||
| 377 | MethodEntry methodEntry = node.getMethodEntry(); | ||
| 378 | if (methodEntries.contains(methodEntry)) { | ||
| 379 | return; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (containsObfMethod(methodEntry)) { | ||
| 383 | AccessFlags flags = getAccessFlags(methodEntry); | ||
| 384 | if (!flags.isPrivate() && !flags.isStatic()) { | ||
| 385 | // collect the entry | ||
| 386 | methodEntries.add(methodEntry); | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | // look at bridge methods! | ||
| 391 | MethodEntry bridgedMethod = getBridgedMethod(methodEntry); | ||
| 392 | while (bridgedMethod != null) { | ||
| 393 | methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod)); | ||
| 394 | bridgedMethod = getBridgedMethod(bridgedMethod); | ||
| 395 | } | ||
| 396 | |||
| 397 | // look at interface methods too | ||
| 398 | for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) { | ||
| 399 | getRelatedMethodImplementations(methodEntries, implementationsNode); | ||
| 400 | } | ||
| 401 | |||
| 402 | // recurse | ||
| 403 | for (int i = 0; i < node.getChildCount(); i++) { | ||
| 404 | getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i)); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { | ||
| 409 | MethodEntry methodEntry = node.getMethodEntry(); | ||
| 410 | if (containsObfMethod(methodEntry)) { | ||
| 411 | AccessFlags flags = getAccessFlags(methodEntry); | ||
| 412 | if (!flags.isPrivate() && !flags.isStatic()) { | ||
| 413 | // collect the entry | ||
| 414 | methodEntries.add(methodEntry); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | // look at bridge methods! | ||
| 419 | MethodEntry bridgedMethod = getBridgedMethod(methodEntry); | ||
| 420 | while (bridgedMethod != null) { | ||
| 421 | methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod)); | ||
| 422 | bridgedMethod = getBridgedMethod(bridgedMethod); | ||
| 423 | } | ||
| 424 | |||
| 425 | // recurse | ||
| 426 | for (int i = 0; i < node.getChildCount(); i++) { | ||
| 427 | getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i)); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | public Collection<EntryReference<FieldEntry, MethodDefEntry>> getFieldReferences(FieldEntry fieldEntry) { | ||
| 432 | return this.fieldReferences.get(fieldEntry); | ||
| 433 | } | ||
| 434 | |||
| 435 | public Collection<FieldEntry> getReferencedFields(MethodDefEntry methodEntry) { | ||
| 436 | // linear search is fast enough for now | ||
| 437 | Set<FieldEntry> fieldEntries = Sets.newHashSet(); | ||
| 438 | for (EntryReference<FieldEntry, MethodDefEntry> reference : this.fieldReferences.values()) { | ||
| 439 | if (reference.context == methodEntry) { | ||
| 440 | fieldEntries.add(reference.entry); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | return fieldEntries; | ||
| 444 | } | ||
| 445 | |||
| 446 | public Collection<EntryReference<ClassEntry, MethodDefEntry>> getMethodsReferencing(ClassEntry classEntry) { | ||
| 447 | return this.methodsReferencingClasses.get(classEntry); | ||
| 448 | } | ||
| 449 | |||
| 450 | @Deprecated | ||
| 451 | public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry) { | ||
| 452 | return getMethodsReferencing(methodEntry, false); | ||
| 453 | } | ||
| 454 | |||
| 455 | public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry, boolean recurse) { | ||
| 456 | if (!recurse) { | ||
| 457 | return this.methodsReferencing.get(methodEntry); | ||
| 458 | } | ||
| 459 | |||
| 460 | List<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>(); | ||
| 461 | Set<MethodEntry> methodEntries = getRelatedMethodImplementations(methodEntry); | ||
| 462 | for (MethodEntry entry : methodEntries) { | ||
| 463 | references.addAll(getMethodsReferencing(entry, false)); | ||
| 464 | } | ||
| 465 | return references; | ||
| 466 | } | ||
| 467 | |||
| 468 | public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) { | ||
| 469 | return this.methodReferences.get(methodEntry); | ||
| 470 | } | ||
| 471 | |||
| 472 | public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { | ||
| 473 | return this.innerClassesByOuter.get(obfOuterClassEntry); | ||
| 474 | } | ||
| 475 | |||
| 476 | public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { | ||
| 477 | return this.outerClassesByInner.get(obfInnerClassEntry); | ||
| 478 | } | ||
| 479 | |||
| 480 | public boolean isSyntheticMethod(MethodEntry methodEntry) { | ||
| 481 | return this.syntheticMethods.contains(methodEntry); | ||
| 482 | } | ||
| 483 | |||
| 484 | public Set<ClassEntry> getInterfaces(String className) { | ||
| 485 | ClassEntry classEntry = entryPool.getClass(className); | ||
| 486 | Set<ClassEntry> interfaces = new HashSet<>(this.translationIndex.getInterfaces(classEntry)); | ||
| 487 | for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { | ||
| 488 | interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); | ||
| 489 | } | ||
| 490 | return interfaces; | ||
| 491 | } | ||
| 492 | |||
| 493 | public Set<String> getImplementingClasses(String targetInterfaceName) { | ||
| 494 | |||
| 495 | // linear search is fast enough for now | ||
| 496 | Set<String> classNames = Sets.newHashSet(); | ||
| 497 | for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) { | ||
| 498 | ClassEntry classEntry = entry.getKey(); | ||
| 499 | ClassEntry interfaceEntry = entry.getValue(); | ||
| 500 | if (interfaceEntry.getName().equals(targetInterfaceName)) { | ||
| 501 | String className = classEntry.getClassName(); | ||
| 502 | classNames.add(className); | ||
| 503 | if (isInterface(className)) { | ||
| 504 | classNames.addAll(getImplementingClasses(className)); | ||
| 505 | } | ||
| 506 | |||
| 507 | this.translationIndex.getSubclassNamesRecursively(classNames, classEntry); | ||
| 508 | } | ||
| 509 | } | ||
| 510 | return classNames; | ||
| 511 | } | ||
| 512 | |||
| 513 | public boolean isInterface(String className) { | ||
| 514 | return this.translationIndex.isInterface(entryPool.getClass(className)); | ||
| 515 | } | ||
| 516 | |||
| 517 | public boolean containsObfClass(ClassEntry obfClassEntry) { | ||
| 518 | return this.obfClassEntries.contains(obfClassEntry); | ||
| 519 | } | ||
| 520 | |||
| 521 | public boolean containsObfField(FieldEntry obfFieldEntry) { | ||
| 522 | return this.access.containsKey(obfFieldEntry); | ||
| 523 | } | ||
| 524 | |||
| 525 | public boolean containsObfMethod(MethodEntry obfMethodEntry) { | ||
| 526 | return this.access.containsKey(obfMethodEntry); | ||
| 527 | } | ||
| 528 | |||
| 529 | public boolean containsEntryWithSameName(Entry entry) { | ||
| 530 | for (Entry target : this.access.keySet()) | ||
| 531 | if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass())) | ||
| 532 | return true; | ||
| 533 | return false; | ||
| 534 | } | ||
| 535 | |||
| 536 | public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) { | ||
| 537 | // check the behavior | ||
| 538 | if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) { | ||
| 539 | return false; | ||
| 540 | } | ||
| 541 | |||
| 542 | return true; | ||
| 543 | } | ||
| 544 | |||
| 545 | public boolean containsObfEntry(Entry obfEntry) { | ||
| 546 | if (obfEntry instanceof ClassEntry) { | ||
| 547 | return containsObfClass((ClassEntry) obfEntry); | ||
| 548 | } else if (obfEntry instanceof FieldEntry) { | ||
| 549 | return containsObfField((FieldEntry) obfEntry); | ||
| 550 | } else if (obfEntry instanceof MethodEntry) { | ||
| 551 | return containsObfMethod((MethodEntry) obfEntry); | ||
| 552 | } else if (obfEntry instanceof LocalVariableEntry) { | ||
| 553 | return containsObfVariable((LocalVariableEntry) obfEntry); | ||
| 554 | } else { | ||
| 555 | throw new Error("Entry desc not supported: " + obfEntry.getClass().getName()); | ||
| 556 | } | ||
| 557 | } | ||
| 558 | |||
| 559 | public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { | ||
| 560 | return this.bridgedMethods.get(bridgeMethodEntry); | ||
| 561 | } | ||
| 562 | |||
| 563 | public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) { | ||
| 564 | |||
| 565 | // build class chain in inner-to-outer order | ||
| 566 | List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); | ||
| 567 | ClassEntry checkClassEntry = obfClassEntry; | ||
| 568 | while (true) { | ||
| 569 | ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); | ||
| 570 | if (obfOuterClassEntry != null) { | ||
| 571 | obfClassChain.add(obfOuterClassEntry); | ||
| 572 | checkClassEntry = obfOuterClassEntry; | ||
| 573 | } else { | ||
| 574 | break; | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | // switch to outer-to-inner order | ||
| 579 | Collections.reverse(obfClassChain); | ||
| 580 | |||
| 581 | return obfClassChain; | ||
| 582 | } | ||
| 583 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 4b47c5f..e4b0304 100644 --- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java | |||
| @@ -12,24 +12,28 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.EntryIndex; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 17 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.translation.Translator; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 18 | 21 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | 22 | import javax.swing.tree.DefaultMutableTreeNode; |
| 23 | import java.util.Collection; | ||
| 20 | import java.util.List; | 24 | import java.util.List; |
| 21 | 25 | ||
| 22 | public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { | 26 | public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { |
| 23 | 27 | ||
| 24 | private Translator deobfuscatingTranslator; | 28 | private final Translator translator; |
| 25 | private MethodEntry entry; | 29 | private MethodEntry entry; |
| 26 | 30 | ||
| 27 | public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { | 31 | public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) { |
| 32 | this.translator = translator; | ||
| 28 | if (entry == null) { | 33 | if (entry == null) { |
| 29 | throw new IllegalArgumentException("Entry cannot be null!"); | 34 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 30 | } | 35 | } |
| 31 | 36 | ||
| 32 | this.deobfuscatingTranslator = deobfuscatingTranslator; | ||
| 33 | this.entry = entry; | 37 | this.entry = entry; |
| 34 | } | 38 | } |
| 35 | 39 | ||
| @@ -53,35 +57,25 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { | |||
| 53 | return this.entry; | 57 | return this.entry; |
| 54 | } | 58 | } |
| 55 | 59 | ||
| 56 | public String getDeobfClassName() { | ||
| 57 | return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getClassName(); | ||
| 58 | } | ||
| 59 | |||
| 60 | public String getDeobfMethodName() { | ||
| 61 | return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | 60 | @Override |
| 65 | public String toString() { | 61 | public String toString() { |
| 66 | String className = getDeobfClassName(); | 62 | MethodEntry translatedEntry = translator.translate(entry); |
| 67 | if (className == null) { | 63 | String className = translatedEntry.getParent().getFullName(); |
| 68 | className = this.entry.getClassName(); | 64 | String methodName = translatedEntry.getName(); |
| 69 | } | ||
| 70 | |||
| 71 | String methodName = getDeobfMethodName(); | ||
| 72 | if (methodName == null) { | ||
| 73 | methodName = this.entry.getName(); | ||
| 74 | } | ||
| 75 | return className + "." + methodName + "()"; | 65 | return className + "." + methodName + "()"; |
| 76 | } | 66 | } |
| 77 | 67 | ||
| 78 | public void load(JarIndex index) { | 68 | public void load(JarIndex index) { |
| 79 | // get all method implementations | 69 | // get all method implementations |
| 80 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); | 70 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); |
| 81 | for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { | 71 | EntryIndex entryIndex = index.getEntryIndex(); |
| 82 | MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getDesc()); | 72 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); |
| 83 | if (index.containsObfMethod(methodEntry)) { | 73 | |
| 84 | nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); | 74 | Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry.getParent()); |
| 75 | for (ClassEntry inheritor : inheritors) { | ||
| 76 | MethodEntry methodEntry = entry.withParent(inheritor); | ||
| 77 | if (entryIndex.hasMethod(methodEntry)) { | ||
| 78 | nodes.add(new MethodImplementationsTreeNode(translator, methodEntry)); | ||
| 85 | } | 79 | } |
| 86 | } | 80 | } |
| 87 | 81 | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index dc34197..f0fd1d2 100644 --- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java | |||
| @@ -12,21 +12,24 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.EntryIndex; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 17 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.translation.Translator; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 18 | 21 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | 22 | import javax.swing.tree.DefaultMutableTreeNode; |
| 20 | import java.util.List; | 23 | import java.util.List; |
| 21 | 24 | ||
| 22 | public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { | 25 | public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { |
| 23 | 26 | ||
| 24 | private Translator deobfuscatingTranslator; | 27 | private final Translator translator; |
| 25 | private MethodEntry entry; | 28 | private MethodEntry entry; |
| 26 | private boolean isImplemented; | 29 | private boolean isImplemented; |
| 27 | 30 | ||
| 28 | public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { | 31 | public MethodInheritanceTreeNode(Translator translator, MethodEntry entry, boolean isImplemented) { |
| 29 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 32 | this.translator = translator; |
| 30 | this.entry = entry; | 33 | this.entry = entry; |
| 31 | this.isImplemented = isImplemented; | 34 | this.isImplemented = isImplemented; |
| 32 | } | 35 | } |
| @@ -51,32 +54,19 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { | |||
| 51 | return this.entry; | 54 | return this.entry; |
| 52 | } | 55 | } |
| 53 | 56 | ||
| 54 | public String getDeobfClassName() { | ||
| 55 | return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getName(); | ||
| 56 | } | ||
| 57 | |||
| 58 | public String getDeobfMethodName() { | ||
| 59 | return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); | ||
| 60 | } | ||
| 61 | |||
| 62 | public boolean isImplemented() { | 57 | public boolean isImplemented() { |
| 63 | return this.isImplemented; | 58 | return this.isImplemented; |
| 64 | } | 59 | } |
| 65 | 60 | ||
| 66 | @Override | 61 | @Override |
| 67 | public String toString() { | 62 | public String toString() { |
| 68 | String className = getDeobfClassName(); | 63 | MethodEntry translatedEntry = translator.translate(entry); |
| 69 | if (className == null) { | 64 | String className = translatedEntry.getContainingClass().getFullName(); |
| 70 | className = this.entry.getClassName(); | ||
| 71 | } | ||
| 72 | 65 | ||
| 73 | if (!this.isImplemented) { | 66 | if (!this.isImplemented) { |
| 74 | return className; | 67 | return className; |
| 75 | } else { | 68 | } else { |
| 76 | String methodName = getDeobfMethodName(); | 69 | String methodName = translatedEntry.getName(); |
| 77 | if (methodName == null) { | ||
| 78 | methodName = this.entry.getName(); | ||
| 79 | } | ||
| 80 | return className + "." + methodName + "()"; | 70 | return className + "." + methodName + "()"; |
| 81 | } | 71 | } |
| 82 | } | 72 | } |
| @@ -84,14 +74,12 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { | |||
| 84 | public void load(JarIndex index, boolean recurse) { | 74 | public void load(JarIndex index, boolean recurse) { |
| 85 | // get all the child nodes | 75 | // get all the child nodes |
| 86 | List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); | 76 | List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); |
| 87 | for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getOwnerClassEntry())) { | 77 | EntryIndex entryIndex = index.getEntryIndex(); |
| 88 | MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); | 78 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); |
| 89 | nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry))); | ||
| 90 | } | ||
| 91 | 79 | ||
| 92 | for (ClassEntry subclassEntry : index.getTranslationIndex().getImplementers(this.entry.getOwnerClassEntry())) { | 80 | for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) { |
| 93 | MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); | 81 | MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc()); |
| 94 | nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry))); | 82 | nodes.add(new MethodInheritanceTreeNode(translator, methodEntry, entryIndex.hasMethod(methodEntry))); |
| 95 | } | 83 | } |
| 96 | 84 | ||
| 97 | // add them to this node | 85 | // add them to this node |
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java index ac05acd..8995eb5 100644 --- a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java | |||
| @@ -12,36 +12,36 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.mapping.*; | 16 | import cuchaz.enigma.analysis.index.ReferenceIndex; |
| 17 | import cuchaz.enigma.mapping.entry.Entry; | 17 | import cuchaz.enigma.translation.Translator; |
| 18 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | 18 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 19 | import cuchaz.enigma.mapping.entry.MethodEntry; | 19 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 20 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 20 | 22 | ||
| 21 | import javax.swing.tree.DefaultMutableTreeNode; | 23 | import javax.swing.tree.DefaultMutableTreeNode; |
| 22 | import javax.swing.tree.TreeNode; | 24 | import javax.swing.tree.TreeNode; |
| 25 | import java.util.ArrayList; | ||
| 26 | import java.util.Collection; | ||
| 23 | import java.util.Set; | 27 | import java.util.Set; |
| 24 | 28 | ||
| 25 | public class MethodReferenceTreeNode extends DefaultMutableTreeNode | 29 | public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<MethodEntry, MethodDefEntry> { |
| 26 | implements ReferenceTreeNode<MethodEntry, MethodDefEntry> { | ||
| 27 | 30 | ||
| 28 | private Translator deobfuscatingTranslator; | 31 | private final Translator translator; |
| 29 | private MethodEntry entry; | 32 | private MethodEntry entry; |
| 30 | private EntryReference<MethodEntry, MethodDefEntry> reference; | 33 | private EntryReference<MethodEntry, MethodDefEntry> reference; |
| 31 | private AccessFlags access; | ||
| 32 | 34 | ||
| 33 | public MethodReferenceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { | 35 | public MethodReferenceTreeNode(Translator translator, MethodEntry entry) { |
| 34 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 36 | this.translator = translator; |
| 35 | this.entry = entry; | 37 | this.entry = entry; |
| 36 | this.reference = null; | 38 | this.reference = null; |
| 37 | } | 39 | } |
| 38 | 40 | ||
| 39 | public MethodReferenceTreeNode(Translator deobfuscatingTranslator, | 41 | public MethodReferenceTreeNode(Translator translator, EntryReference<MethodEntry, MethodDefEntry> reference) { |
| 40 | EntryReference<MethodEntry, MethodDefEntry> reference, AccessFlags access) { | 42 | this.translator = translator; |
| 41 | this.deobfuscatingTranslator = deobfuscatingTranslator; | ||
| 42 | this.entry = reference.entry; | 43 | this.entry = reference.entry; |
| 43 | this.reference = reference; | 44 | this.reference = reference; |
| 44 | this.access = access; | ||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | @Override | 47 | @Override |
| @@ -57,21 +57,17 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode | |||
| 57 | @Override | 57 | @Override |
| 58 | public String toString() { | 58 | public String toString() { |
| 59 | if (this.reference != null) { | 59 | if (this.reference != null) { |
| 60 | return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), | 60 | return String.format("%s", translator.translate(this.reference.context)); |
| 61 | this.access); | ||
| 62 | } | 61 | } |
| 63 | return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); | 62 | return translator.translate(this.entry).getName(); |
| 64 | } | ||
| 65 | |||
| 66 | @Deprecated | ||
| 67 | public void load(JarIndex index, boolean recurse) { | ||
| 68 | load(index, recurse, false); | ||
| 69 | } | 63 | } |
| 70 | 64 | ||
| 71 | public void load(JarIndex index, boolean recurse, boolean recurseMethod) { | 65 | public void load(JarIndex index, boolean recurse, boolean recurseMethod) { |
| 72 | // get all the child nodes | 66 | // get all the child nodes |
| 73 | for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry, recurseMethod)) { | 67 | Collection<EntryReference<MethodEntry, MethodDefEntry>> references = getReferences(index, recurseMethod); |
| 74 | add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); | 68 | |
| 69 | for (EntryReference<MethodEntry, MethodDefEntry> reference : references) { | ||
| 70 | add(new MethodReferenceTreeNode(translator, reference)); | ||
| 75 | } | 71 | } |
| 76 | 72 | ||
| 77 | if (recurse && this.children != null) { | 73 | if (recurse && this.children != null) { |
| @@ -80,7 +76,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode | |||
| 80 | MethodReferenceTreeNode node = (MethodReferenceTreeNode) child; | 76 | MethodReferenceTreeNode node = (MethodReferenceTreeNode) child; |
| 81 | 77 | ||
| 82 | // don't recurse into ancestor | 78 | // don't recurse into ancestor |
| 83 | Set<Entry> ancestors = Sets.newHashSet(); | 79 | Set<Entry<?>> ancestors = Sets.newHashSet(); |
| 84 | TreeNode n = node; | 80 | TreeNode n = node; |
| 85 | while (n.getParent() != null) { | 81 | while (n.getParent() != null) { |
| 86 | n = n.getParent(); | 82 | n = n.getParent(); |
| @@ -92,9 +88,26 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode | |||
| 92 | continue; | 88 | continue; |
| 93 | } | 89 | } |
| 94 | 90 | ||
| 95 | node.load(index, true); | 91 | node.load(index, true, false); |
| 96 | } | 92 | } |
| 97 | } | 93 | } |
| 98 | } | 94 | } |
| 99 | } | 95 | } |
| 96 | |||
| 97 | private Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferences(JarIndex index, boolean recurseMethod) { | ||
| 98 | ReferenceIndex referenceIndex = index.getReferenceIndex(); | ||
| 99 | |||
| 100 | if (recurseMethod) { | ||
| 101 | Collection<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>(); | ||
| 102 | |||
| 103 | EntryResolver entryResolver = index.getEntryResolver(); | ||
| 104 | for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) { | ||
| 105 | references.addAll(referenceIndex.getReferencesToMethod(methodEntry)); | ||
| 106 | } | ||
| 107 | |||
| 108 | return references; | ||
| 109 | } else { | ||
| 110 | return referenceIndex.getReferencesToMethod(entry); | ||
| 111 | } | ||
| 112 | } | ||
| 100 | } | 113 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java index 86655d0..ad3aceb 100644 --- a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java +++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java | |||
| @@ -12,11 +12,12 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.io.ByteStreams; | 14 | import com.google.common.io.ByteStreams; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 16 | import org.objectweb.asm.ClassReader; | 16 | import org.objectweb.asm.ClassReader; |
| 17 | import org.objectweb.asm.ClassVisitor; | 17 | import org.objectweb.asm.ClassVisitor; |
| 18 | import org.objectweb.asm.tree.ClassNode; | 18 | import org.objectweb.asm.tree.ClassNode; |
| 19 | 19 | ||
| 20 | import javax.annotation.Nullable; | ||
| 20 | import java.io.BufferedInputStream; | 21 | import java.io.BufferedInputStream; |
| 21 | import java.io.IOException; | 22 | import java.io.IOException; |
| 22 | import java.io.InputStream; | 23 | import java.io.InputStream; |
| @@ -100,9 +101,14 @@ public class ParsedJar { | |||
| 100 | return entries; | 101 | return entries; |
| 101 | } | 102 | } |
| 102 | 103 | ||
| 104 | @Nullable | ||
| 103 | public ClassNode getClassNode(String name) { | 105 | public ClassNode getClassNode(String name) { |
| 104 | return nodeCache.computeIfAbsent(name, (n) -> { | 106 | return nodeCache.computeIfAbsent(name, (n) -> { |
| 105 | ClassReader reader = new ClassReader(classBytes.get(name)); | 107 | byte[] bytes = classBytes.get(name); |
| 108 | if (bytes == null) { | ||
| 109 | return null; | ||
| 110 | } | ||
| 111 | ClassReader reader = new ClassReader(bytes); | ||
| 106 | ClassNode node = new ClassNode(); | 112 | ClassNode node = new ClassNode(); |
| 107 | reader.accept(node, 0); | 113 | reader.accept(node, 0); |
| 108 | return node; | 114 | return node; |
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java index 3950d16..c0a3a75 100644 --- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java | |||
| @@ -11,9 +11,9 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.Entry; | 14 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 15 | 15 | ||
| 16 | public interface ReferenceTreeNode<E extends Entry, C extends Entry> { | 16 | public interface ReferenceTreeNode<E extends Entry<?>, C extends Entry<?>> { |
| 17 | E getEntry(); | 17 | E getEntry(); |
| 18 | 18 | ||
| 19 | EntryReference<E, C> getReference(); | 19 | EntryReference<E, C> getReference(); |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java index 3e0d66b..abdec92 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java | |||
| @@ -11,17 +11,15 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.*; |
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.google.common.collect.Maps; | ||
| 17 | import com.google.common.collect.Multimap; | ||
| 18 | import com.strobel.decompiler.languages.Region; | 15 | import com.strobel.decompiler.languages.Region; |
| 19 | import com.strobel.decompiler.languages.java.ast.AstNode; | 16 | import com.strobel.decompiler.languages.java.ast.AstNode; |
| 20 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; | 17 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; |
| 21 | import com.strobel.decompiler.languages.java.ast.Identifier; | 18 | import com.strobel.decompiler.languages.java.ast.Identifier; |
| 22 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | 19 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; |
| 23 | import cuchaz.enigma.mapping.entry.Entry; | 20 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 24 | 21 | ||
| 22 | import javax.annotation.Nullable; | ||
| 25 | import java.util.*; | 23 | import java.util.*; |
| 26 | import java.util.regex.Pattern; | 24 | import java.util.regex.Pattern; |
| 27 | 25 | ||
| @@ -29,9 +27,9 @@ public class SourceIndex { | |||
| 29 | private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); | 27 | private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); |
| 30 | 28 | ||
| 31 | private String source; | 29 | private String source; |
| 32 | private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; | 30 | private TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference; |
| 33 | private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; | 31 | private Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens; |
| 34 | private Map<Entry, Token> declarationToToken; | 32 | private Map<Entry<?>, Token> declarationToToken; |
| 35 | private List<Integer> lineOffsets; | 33 | private List<Integer> lineOffsets; |
| 36 | private boolean ignoreBadTokens; | 34 | private boolean ignoreBadTokens; |
| 37 | 35 | ||
| @@ -42,7 +40,7 @@ public class SourceIndex { | |||
| 42 | public SourceIndex(String source, boolean ignoreBadTokens) { | 40 | public SourceIndex(String source, boolean ignoreBadTokens) { |
| 43 | this.source = source; | 41 | this.source = source; |
| 44 | this.ignoreBadTokens = ignoreBadTokens; | 42 | this.ignoreBadTokens = ignoreBadTokens; |
| 45 | this.tokenToReference = Maps.newTreeMap(); | 43 | this.tokenToReference = new TreeMap<>(); |
| 46 | this.referenceToTokens = HashMultimap.create(); | 44 | this.referenceToTokens = HashMultimap.create(); |
| 47 | this.declarationToToken = Maps.newHashMap(); | 45 | this.declarationToToken = Maps.newHashMap(); |
| 48 | calculateLineOffsets(); | 46 | calculateLineOffsets(); |
| @@ -63,12 +61,12 @@ public class SourceIndex { | |||
| 63 | this.source = source; | 61 | this.source = source; |
| 64 | calculateLineOffsets(); | 62 | calculateLineOffsets(); |
| 65 | 63 | ||
| 66 | for (Entry entry : Lists.newArrayList(declarationToToken.keySet())) { | 64 | for (Entry<?> entry : Lists.newArrayList(declarationToToken.keySet())) { |
| 67 | Token token = declarationToToken.get(entry); | 65 | Token token = declarationToToken.get(entry); |
| 68 | declarationToToken.put(entry, tokenMap.getOrDefault(token, token)); | 66 | declarationToToken.put(entry, tokenMap.getOrDefault(token, token)); |
| 69 | } | 67 | } |
| 70 | 68 | ||
| 71 | for (EntryReference<Entry, Entry> ref : referenceToTokens.keySet()) { | 69 | for (EntryReference<Entry<?>, Entry<?>> ref : referenceToTokens.keySet()) { |
| 72 | Collection<Token> oldTokens = referenceToTokens.get(ref); | 70 | Collection<Token> oldTokens = referenceToTokens.get(ref); |
| 73 | List<Token> newTokens = new ArrayList<>(oldTokens.size()); | 71 | List<Token> newTokens = new ArrayList<>(oldTokens.size()); |
| 74 | 72 | ||
| @@ -79,7 +77,8 @@ public class SourceIndex { | |||
| 79 | referenceToTokens.replaceValues(ref, newTokens); | 77 | referenceToTokens.replaceValues(ref, newTokens); |
| 80 | } | 78 | } |
| 81 | 79 | ||
| 82 | Map<Token, EntryReference<Entry, Entry>> tokenToReferenceCopy = Maps.newHashMap(tokenToReference); | 80 | TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReferenceCopy = new TreeMap<>(tokenToReference); |
| 81 | |||
| 83 | tokenToReference.clear(); | 82 | tokenToReference.clear(); |
| 84 | for (Token token : tokenToReferenceCopy.keySet()) { | 83 | for (Token token : tokenToReferenceCopy.keySet()) { |
| 85 | tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token)); | 84 | tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token)); |
| @@ -112,9 +111,9 @@ public class SourceIndex { | |||
| 112 | return null; | 111 | return null; |
| 113 | } | 112 | } |
| 114 | 113 | ||
| 115 | if (node instanceof Identifier && name.indexOf('$') >=0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()){ | 114 | if (node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()) { |
| 116 | TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; | 115 | TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; |
| 117 | if (type != null){ | 116 | if (type != null) { |
| 118 | name = type.getName(); | 117 | name = type.getName(); |
| 119 | token.end = token.start + name.length(); | 118 | token.end = token.start + name.length(); |
| 120 | } | 119 | } |
| @@ -133,19 +132,19 @@ public class SourceIndex { | |||
| 133 | return token; | 132 | return token; |
| 134 | } | 133 | } |
| 135 | 134 | ||
| 136 | public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { | 135 | public void addReference(AstNode node, Entry<?> deobfEntry, Entry<?> deobfContext) { |
| 137 | Token token = getToken(node); | 136 | Token token = getToken(node); |
| 138 | if (token != null) { | 137 | if (token != null) { |
| 139 | EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); | 138 | EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); |
| 140 | this.tokenToReference.put(token, deobfReference); | 139 | this.tokenToReference.put(token, deobfReference); |
| 141 | this.referenceToTokens.put(deobfReference, token); | 140 | this.referenceToTokens.put(deobfReference, token); |
| 142 | } | 141 | } |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | public void addDeclaration(AstNode node, Entry deobfEntry) { | 144 | public void addDeclaration(AstNode node, Entry<?> deobfEntry) { |
| 146 | Token token = getToken(node); | 145 | Token token = getToken(node); |
| 147 | if (token != null) { | 146 | if (token != null) { |
| 148 | EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); | 147 | EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text); |
| 149 | this.tokenToReference.put(token, reference); | 148 | this.tokenToReference.put(token, reference); |
| 150 | this.referenceToTokens.put(reference, token); | 149 | this.referenceToTokens.put(reference, token); |
| 151 | this.declarationToToken.put(deobfEntry, token); | 150 | this.declarationToToken.put(deobfEntry, token); |
| @@ -160,22 +159,22 @@ public class SourceIndex { | |||
| 160 | return null; | 159 | return null; |
| 161 | } | 160 | } |
| 162 | 161 | ||
| 163 | public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { | 162 | public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) { |
| 164 | return this.referenceToTokens.get(deobfReference); | 163 | return this.referenceToTokens.get(deobfReference); |
| 165 | } | 164 | } |
| 166 | 165 | ||
| 167 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { | 166 | @Nullable |
| 167 | public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) { | ||
| 168 | if (token == null) { | 168 | if (token == null) { |
| 169 | return null; | 169 | return null; |
| 170 | } | 170 | } |
| 171 | return this.tokenToReference.get(token); | 171 | return this.tokenToReference.get(token); |
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { | 174 | public void replaceDeobfReference(Token token, EntryReference<Entry<?>, Entry<?>> newDeobfReference) { |
| 175 | EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); | 175 | EntryReference<Entry<?>, Entry<?>> oldDeobfReferences = this.tokenToReference.replace(token, newDeobfReference); |
| 176 | this.tokenToReference.put(token, newDeobfReference); | 176 | |
| 177 | Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); | 177 | Collection<Token> tokens = this.referenceToTokens.removeAll(oldDeobfReferences); |
| 178 | this.referenceToTokens.removeAll(oldDeobfReference); | ||
| 179 | this.referenceToTokens.putAll(newDeobfReference, tokens); | 178 | this.referenceToTokens.putAll(newDeobfReference, tokens); |
| 180 | } | 179 | } |
| 181 | 180 | ||
| @@ -187,11 +186,11 @@ public class SourceIndex { | |||
| 187 | return this.declarationToToken.values(); | 186 | return this.declarationToToken.values(); |
| 188 | } | 187 | } |
| 189 | 188 | ||
| 190 | public Iterable<Entry> declarations() { | 189 | public Iterable<Entry<?>> declarations() { |
| 191 | return this.declarationToToken.keySet(); | 190 | return this.declarationToToken.keySet(); |
| 192 | } | 191 | } |
| 193 | 192 | ||
| 194 | public Token getDeclarationToken(Entry deobfEntry) { | 193 | public Token getDeclarationToken(Entry<?> deobfEntry) { |
| 195 | return this.declarationToToken.get(deobfEntry); | 194 | return this.declarationToToken.get(deobfEntry); |
| 196 | } | 195 | } |
| 197 | 196 | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index cad0857..486603c 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | |||
| @@ -17,9 +17,12 @@ import com.strobel.assembler.metadata.TypeDefinition; | |||
| 17 | import com.strobel.assembler.metadata.TypeReference; | 17 | import com.strobel.assembler.metadata.TypeReference; |
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.bytecode.AccessFlags; | 20 | import cuchaz.enigma.translation.representation.ProcyonEntryFactory; |
| 21 | import cuchaz.enigma.mapping.Signature; | 21 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; |
| 22 | import cuchaz.enigma.mapping.entry.*; | 22 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 23 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 24 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 25 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 23 | 26 | ||
| 24 | public class SourceIndexClassVisitor extends SourceIndexVisitor { | 27 | public class SourceIndexClassVisitor extends SourceIndexVisitor { |
| 25 | private final ReferencedEntryPool entryPool; | 28 | private final ReferencedEntryPool entryPool; |
| @@ -37,7 +40,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 37 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { | 40 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { |
| 38 | // is this this class, or a subtype? | 41 | // is this this class, or a subtype? |
| 39 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); | 42 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); |
| 40 | ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); | 43 | ClassDefEntry classEntry = ClassDefEntry.parse(def); |
| 41 | if (!classEntry.equals(this.classEntry)) { | 44 | if (!classEntry.equals(this.classEntry)) { |
| 42 | // it's a subtype, recurse | 45 | // it's a subtype, recurse |
| 43 | index.addDeclaration(node.getNameToken(), classEntry); | 46 | index.addDeclaration(node.getNameToken(), classEntry); |
| @@ -68,7 +71,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 68 | tokenNode = node.getModifiers().firstOrNullObject(); | 71 | tokenNode = node.getModifiers().firstOrNullObject(); |
| 69 | } | 72 | } |
| 70 | index.addDeclaration(tokenNode, methodEntry); | 73 | index.addDeclaration(tokenNode, methodEntry); |
| 71 | return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); | 74 | return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); |
| 72 | } | 75 | } |
| 73 | 76 | ||
| 74 | @Override | 77 | @Override |
| @@ -76,7 +79,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 76 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); | 79 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); |
| 77 | MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); | 80 | MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); |
| 78 | index.addDeclaration(node.getNameToken(), methodEntry); | 81 | index.addDeclaration(node.getNameToken(), methodEntry); |
| 79 | return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); | 82 | return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); |
| 80 | } | 83 | } |
| 81 | 84 | ||
| 82 | @Override | 85 | @Override |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java index 139fcea..73db28f 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java | |||
| @@ -17,8 +17,9 @@ import com.strobel.assembler.metadata.*; | |||
| 17 | import com.strobel.decompiler.ast.Variable; | 17 | import com.strobel.decompiler.ast.Variable; |
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.mapping.TypeDescriptor; | 20 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 21 | import cuchaz.enigma.mapping.entry.*; | 21 | import cuchaz.enigma.translation.representation.*; |
| 22 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 22 | 23 | ||
| 23 | import java.lang.Error; | 24 | import java.lang.Error; |
| 24 | import java.util.HashMap; | 25 | import java.util.HashMap; |
| @@ -26,19 +27,15 @@ import java.util.Map; | |||
| 26 | 27 | ||
| 27 | public class SourceIndexMethodVisitor extends SourceIndexVisitor { | 28 | public class SourceIndexMethodVisitor extends SourceIndexVisitor { |
| 28 | private final ReferencedEntryPool entryPool; | 29 | private final ReferencedEntryPool entryPool; |
| 29 | private final ProcyonEntryFactory entryFactory; | ||
| 30 | 30 | ||
| 31 | private final ClassDefEntry ownerEntry; | ||
| 32 | private final MethodDefEntry methodEntry; | 31 | private final MethodDefEntry methodEntry; |
| 33 | 32 | ||
| 34 | private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); | 33 | private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); |
| 35 | private Map<String, Entry> identifierEntryCache = new HashMap<>(); | 34 | private Map<String, Entry<?>> identifierEntryCache = new HashMap<>(); |
| 36 | 35 | ||
| 37 | public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, ClassDefEntry ownerEntry, MethodDefEntry methodEntry) { | 36 | public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry) { |
| 38 | super(entryPool); | 37 | super(entryPool); |
| 39 | this.entryPool = entryPool; | 38 | this.entryPool = entryPool; |
| 40 | this.entryFactory = new ProcyonEntryFactory(entryPool); | ||
| 41 | this.ownerEntry = ownerEntry; | ||
| 42 | this.methodEntry = methodEntry; | 39 | this.methodEntry = methodEntry; |
| 43 | } | 40 | } |
| 44 | 41 | ||
| @@ -86,7 +83,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 86 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); | 83 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); |
| 87 | FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); | 84 | FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); |
| 88 | if (fieldEntry == null) { | 85 | if (fieldEntry == null) { |
| 89 | throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); | 86 | throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName()); |
| 90 | } | 87 | } |
| 91 | index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); | 88 | index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); |
| 92 | } | 89 | } |
| @@ -128,7 +125,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 128 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); | 125 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); |
| 129 | FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); | 126 | FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); |
| 130 | if (fieldEntry == null) { | 127 | if (fieldEntry == null) { |
| 131 | throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); | 128 | throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName()); |
| 132 | } | 129 | } |
| 133 | index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); | 130 | index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); |
| 134 | } else | 131 | } else |
| @@ -144,7 +141,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 144 | } | 141 | } |
| 145 | 142 | ||
| 146 | private void addDeclarationToUnmatched(String key, SourceIndex index) { | 143 | private void addDeclarationToUnmatched(String key, SourceIndex index) { |
| 147 | Entry entry = identifierEntryCache.get(key); | 144 | Entry<?> entry = identifierEntryCache.get(key); |
| 148 | 145 | ||
| 149 | // This cannot happened in theory | 146 | // This cannot happened in theory |
| 150 | if (entry == null) | 147 | if (entry == null) |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java index e588d24..564830c 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java | |||
| @@ -14,10 +14,8 @@ package cuchaz.enigma.analysis; | |||
| 14 | import com.strobel.assembler.metadata.TypeDefinition; | 14 | import com.strobel.assembler.metadata.TypeDefinition; |
| 15 | import com.strobel.decompiler.languages.java.ast.*; | 15 | import com.strobel.decompiler.languages.java.ast.*; |
| 16 | import com.strobel.decompiler.patterns.Pattern; | 16 | import com.strobel.decompiler.patterns.Pattern; |
| 17 | import cuchaz.enigma.bytecode.AccessFlags; | 17 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; |
| 18 | import cuchaz.enigma.mapping.Signature; | 18 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 19 | import cuchaz.enigma.mapping.entry.ClassDefEntry; | ||
| 20 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | ||
| 21 | 19 | ||
| 22 | public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { | 20 | public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { |
| 23 | private final ReferencedEntryPool entryPool; | 21 | private final ReferencedEntryPool entryPool; |
| @@ -29,7 +27,7 @@ public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { | |||
| 29 | @Override | 27 | @Override |
| 30 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { | 28 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { |
| 31 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); | 29 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); |
| 32 | ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); | 30 | ClassDefEntry classEntry = ClassDefEntry.parse(def); |
| 33 | index.addDeclaration(node.getNameToken(), classEntry); | 31 | index.addDeclaration(node.getNameToken(), classEntry); |
| 34 | 32 | ||
| 35 | return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); | 33 | return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); |
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java deleted file mode 100644 index 984d84b..0000000 --- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java +++ /dev/null | |||
| @@ -1,275 +0,0 @@ | |||
| 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; | ||
| 13 | |||
| 14 | import com.google.common.cache.Cache; | ||
| 15 | import com.google.common.cache.CacheBuilder; | ||
| 16 | import com.google.common.collect.HashMultimap; | ||
| 17 | import com.google.common.collect.Lists; | ||
| 18 | import com.google.common.collect.Maps; | ||
| 19 | import com.google.common.collect.Multimap; | ||
| 20 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 21 | import cuchaz.enigma.mapping.*; | ||
| 22 | import cuchaz.enigma.mapping.entry.*; | ||
| 23 | |||
| 24 | import java.util.*; | ||
| 25 | |||
| 26 | public class TranslationIndex { | ||
| 27 | |||
| 28 | private final ReferencedEntryPool entryPool; | ||
| 29 | private Map<ClassEntry, ClassEntry> superclasses; | ||
| 30 | private Map<Entry, DefEntry> defEntries = new HashMap<>(); | ||
| 31 | private Multimap<ClassEntry, FieldDefEntry> fieldEntries; | ||
| 32 | private Multimap<ClassEntry, MethodDefEntry> methodEntries; | ||
| 33 | private Multimap<ClassEntry, ClassEntry> interfaces; | ||
| 34 | |||
| 35 | public TranslationIndex(ReferencedEntryPool entryPool) { | ||
| 36 | this.entryPool = entryPool; | ||
| 37 | this.superclasses = Maps.newHashMap(); | ||
| 38 | this.fieldEntries = HashMultimap.create(); | ||
| 39 | this.methodEntries = HashMultimap.create(); | ||
| 40 | this.interfaces = HashMultimap.create(); | ||
| 41 | |||
| 42 | for (FieldDefEntry entry : fieldEntries.values()) { | ||
| 43 | defEntries.put(entry, entry); | ||
| 44 | } | ||
| 45 | |||
| 46 | for (MethodDefEntry entry : methodEntries.values()) { | ||
| 47 | defEntries.put(entry, entry); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | public TranslationIndex(TranslationIndex other, Translator translator) { | ||
| 52 | this.entryPool = other.entryPool; | ||
| 53 | |||
| 54 | // translate the superclasses | ||
| 55 | this.superclasses = Maps.newHashMap(); | ||
| 56 | for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { | ||
| 57 | this.superclasses.put(translator.getTranslatedClass(mapEntry.getKey()), translator.getTranslatedClass(mapEntry.getValue())); | ||
| 58 | } | ||
| 59 | |||
| 60 | // translate the interfaces | ||
| 61 | this.interfaces = HashMultimap.create(); | ||
| 62 | for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { | ||
| 63 | this.interfaces.put( | ||
| 64 | translator.getTranslatedClass(mapEntry.getKey()), | ||
| 65 | translator.getTranslatedClass(mapEntry.getValue()) | ||
| 66 | ); | ||
| 67 | } | ||
| 68 | |||
| 69 | // translate the fields | ||
| 70 | this.fieldEntries = HashMultimap.create(); | ||
| 71 | for (Map.Entry<ClassEntry, FieldDefEntry> mapEntry : other.fieldEntries.entries()) { | ||
| 72 | this.fieldEntries.put( | ||
| 73 | translator.getTranslatedClass(mapEntry.getKey()), | ||
| 74 | translator.getTranslatedFieldDef(mapEntry.getValue()) | ||
| 75 | ); | ||
| 76 | } | ||
| 77 | |||
| 78 | this.methodEntries = HashMultimap.create(); | ||
| 79 | for (Map.Entry<ClassEntry, MethodDefEntry> mapEntry : other.methodEntries.entries()) { | ||
| 80 | this.methodEntries.put( | ||
| 81 | translator.getTranslatedClass(mapEntry.getKey()), | ||
| 82 | translator.getTranslatedMethodDef(mapEntry.getValue()) | ||
| 83 | ); | ||
| 84 | } | ||
| 85 | |||
| 86 | for (FieldDefEntry entry : fieldEntries.values()) { | ||
| 87 | defEntries.put(entry, entry); | ||
| 88 | } | ||
| 89 | |||
| 90 | for (MethodDefEntry entry : methodEntries.values()) { | ||
| 91 | defEntries.put(entry, entry); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) { | ||
| 96 | ClassDefEntry classEntry = new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access)); | ||
| 97 | if (isJre(classEntry)) { | ||
| 98 | return null; | ||
| 99 | } | ||
| 100 | |||
| 101 | // add the superclass | ||
| 102 | ClassEntry superclassEntry = entryPool.getClass(superName); | ||
| 103 | if (superclassEntry != null) { | ||
| 104 | this.superclasses.put(classEntry, superclassEntry); | ||
| 105 | } | ||
| 106 | |||
| 107 | // add the interfaces | ||
| 108 | for (String interfaceClassName : interfaces) { | ||
| 109 | ClassEntry interfaceClassEntry = entryPool.getClass(interfaceClassName); | ||
| 110 | if (!isJre(interfaceClassEntry)) { | ||
| 111 | this.interfaces.put(classEntry, interfaceClassEntry); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | return classEntry; | ||
| 116 | } | ||
| 117 | |||
| 118 | protected void indexField(FieldDefEntry fieldEntry) { | ||
| 119 | this.fieldEntries.put(fieldEntry.getOwnerClassEntry(), fieldEntry); | ||
| 120 | this.defEntries.put(fieldEntry, fieldEntry); | ||
| 121 | } | ||
| 122 | |||
| 123 | protected void indexMethod(MethodDefEntry methodEntry) { | ||
| 124 | this.methodEntries.put(methodEntry.getOwnerClassEntry(), methodEntry); | ||
| 125 | this.defEntries.put(methodEntry, methodEntry); | ||
| 126 | } | ||
| 127 | |||
| 128 | public void renameClasses(Map<String, String> renames) { | ||
| 129 | EntryRenamer.renameClassesInMap(renames, this.superclasses); | ||
| 130 | EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); | ||
| 131 | EntryRenamer.renameClassesInMultimap(renames, this.methodEntries); | ||
| 132 | |||
| 133 | this.defEntries.clear(); | ||
| 134 | for (FieldDefEntry entry : fieldEntries.values()) { | ||
| 135 | defEntries.put(entry, entry); | ||
| 136 | } | ||
| 137 | |||
| 138 | for (MethodDefEntry entry : methodEntries.values()) { | ||
| 139 | defEntries.put(entry, entry); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | public ClassEntry getSuperclass(ClassEntry classEntry) { | ||
| 144 | return this.superclasses.get(classEntry); | ||
| 145 | } | ||
| 146 | |||
| 147 | public List<ClassEntry> getAncestry(ClassEntry classEntry) { | ||
| 148 | List<ClassEntry> ancestors = Lists.newArrayList(); | ||
| 149 | while (classEntry != null) { | ||
| 150 | classEntry = getSuperclass(classEntry); | ||
| 151 | if (classEntry != null) { | ||
| 152 | ancestors.add(classEntry); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | return ancestors; | ||
| 156 | } | ||
| 157 | |||
| 158 | public List<ClassEntry> getImplementers(ClassEntry classEntry) { | ||
| 159 | // linear search is fast enough for now | ||
| 160 | List<ClassEntry> implementers = Lists.newArrayList(); | ||
| 161 | for (ClassEntry itf : this.interfaces.keySet()) { | ||
| 162 | if (this.interfaces.containsEntry(itf, classEntry)) { | ||
| 163 | implementers.add(itf); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | return implementers; | ||
| 167 | } | ||
| 168 | |||
| 169 | public List<ClassEntry> getSubclass(ClassEntry classEntry) { | ||
| 170 | // linear search is fast enough for now | ||
| 171 | List<ClassEntry> subclasses = Lists.newArrayList(); | ||
| 172 | for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) { | ||
| 173 | ClassEntry subclass = entry.getKey(); | ||
| 174 | ClassEntry superclass = entry.getValue(); | ||
| 175 | if (classEntry.equals(superclass)) { | ||
| 176 | subclasses.add(subclass); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | return subclasses; | ||
| 180 | } | ||
| 181 | |||
| 182 | public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) { | ||
| 183 | for (ClassEntry subclassEntry : getSubclass(classEntry)) { | ||
| 184 | out.add(subclassEntry); | ||
| 185 | getSubclassesRecursively(out, subclassEntry); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) { | ||
| 190 | for (ClassEntry subclassEntry : getSubclass(classEntry)) { | ||
| 191 | out.add(subclassEntry.getName()); | ||
| 192 | getSubclassNamesRecursively(out, subclassEntry); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() { | ||
| 197 | return this.interfaces.entries(); | ||
| 198 | } | ||
| 199 | |||
| 200 | public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) { | ||
| 201 | return this.interfaces.get(classEntry); | ||
| 202 | } | ||
| 203 | |||
| 204 | public boolean isInterface(ClassEntry classEntry) { | ||
| 205 | return this.interfaces.containsValue(classEntry); | ||
| 206 | } | ||
| 207 | |||
| 208 | public boolean entryExists(Entry entry) { | ||
| 209 | if (entry == null) { | ||
| 210 | return false; | ||
| 211 | } | ||
| 212 | |||
| 213 | if (entry instanceof FieldEntry) { | ||
| 214 | return fieldExists((FieldEntry) entry); | ||
| 215 | } else if (entry instanceof MethodEntry) { | ||
| 216 | return methodExists((MethodEntry) entry); | ||
| 217 | } else if (entry instanceof LocalVariableEntry) { | ||
| 218 | return methodExists(((LocalVariableEntry) entry).getOwnerEntry()); | ||
| 219 | } | ||
| 220 | throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); | ||
| 221 | } | ||
| 222 | |||
| 223 | public boolean fieldExists(FieldEntry fieldEntry) { | ||
| 224 | return this.fieldEntries.containsEntry(fieldEntry.getOwnerClassEntry(), fieldEntry); | ||
| 225 | } | ||
| 226 | |||
| 227 | public boolean methodExists(MethodEntry methodEntry) { | ||
| 228 | return this.methodEntries.containsEntry(methodEntry.getOwnerClassEntry(), methodEntry); | ||
| 229 | } | ||
| 230 | |||
| 231 | public ClassEntry resolveEntryOwner(Entry entry) { | ||
| 232 | if (entry instanceof ClassEntry) { | ||
| 233 | return (ClassEntry) entry; | ||
| 234 | } | ||
| 235 | |||
| 236 | if (entryExists(entry)) { | ||
| 237 | return entry.getOwnerClassEntry(); | ||
| 238 | } | ||
| 239 | |||
| 240 | DefEntry def = defEntries.get(entry); | ||
| 241 | if (def != null && (def.getAccess().isPrivate())) { | ||
| 242 | return null; | ||
| 243 | } | ||
| 244 | |||
| 245 | // if we're protected/public/non-static, chances are we're somewhere down | ||
| 246 | LinkedList<ClassEntry> classEntries = new LinkedList<>(); | ||
| 247 | classEntries.add(entry.getOwnerClassEntry()); | ||
| 248 | while (!classEntries.isEmpty()) { | ||
| 249 | ClassEntry c = classEntries.remove(); | ||
| 250 | Entry cEntry = entry.updateOwnership(c); | ||
| 251 | |||
| 252 | if (entryExists(cEntry)) { | ||
| 253 | def = defEntries.get(cEntry); | ||
| 254 | if (def == null || (!def.getAccess().isPrivate())) { | ||
| 255 | return cEntry.getOwnerClassEntry(); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | ClassEntry superC = getSuperclass(c); | ||
| 260 | if (superC != null) { | ||
| 261 | classEntries.add(superC); | ||
| 262 | } | ||
| 263 | if (entry instanceof MethodEntry) { | ||
| 264 | classEntries.addAll(getInterfaces(c)); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | return null; | ||
| 269 | } | ||
| 270 | |||
| 271 | private boolean isJre(ClassEntry classEntry) { | ||
| 272 | String packageName = classEntry.getPackageName(); | ||
| 273 | return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); | ||
| 274 | } | ||
| 275 | } | ||
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/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java index 4d5e803..f9cb23c 100644 --- a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java | |||
| @@ -1,43 +1,40 @@ | |||
| 1 | package cuchaz.enigma.analysis; | 1 | package cuchaz.enigma.analysis.index; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.mapping.entry.ClassDefEntry; | 3 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 4 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 4 | import org.objectweb.asm.ClassVisitor; | 6 | import org.objectweb.asm.ClassVisitor; |
| 5 | import org.objectweb.asm.FieldVisitor; | 7 | import org.objectweb.asm.FieldVisitor; |
| 6 | import org.objectweb.asm.MethodVisitor; | 8 | import org.objectweb.asm.MethodVisitor; |
| 7 | 9 | ||
| 8 | public class IndexClassVisitor extends ClassVisitor { | 10 | public class IndexClassVisitor extends ClassVisitor { |
| 9 | private final JarIndex index; | 11 | private final JarIndexer indexer; |
| 10 | private ClassDefEntry classEntry; | 12 | private ClassDefEntry classEntry; |
| 11 | 13 | ||
| 12 | public IndexClassVisitor(JarIndex index, int api) { | 14 | public IndexClassVisitor(JarIndex indexer, int api) { |
| 13 | super(api); | 15 | super(api); |
| 14 | this.index = index; | 16 | this.indexer = indexer; |
| 15 | } | ||
| 16 | |||
| 17 | public IndexClassVisitor(JarIndex index, int api, ClassVisitor cv) { | ||
| 18 | super(api, cv); | ||
| 19 | this.index = index; | ||
| 20 | } | 17 | } |
| 21 | 18 | ||
| 22 | @Override | 19 | @Override |
| 23 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | 20 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { |
| 24 | this.classEntry = this.index.indexClass(access, name, signature, superName, interfaces); | 21 | classEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces); |
| 22 | indexer.indexClass(classEntry); | ||
| 23 | |||
| 25 | super.visit(version, access, name, signature, superName, interfaces); | 24 | super.visit(version, access, name, signature, superName, interfaces); |
| 26 | } | 25 | } |
| 27 | 26 | ||
| 28 | @Override | 27 | @Override |
| 29 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { | 28 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { |
| 30 | if (this.classEntry != null) { | 29 | indexer.indexField(FieldDefEntry.parse(classEntry, access, name, desc, signature)); |
| 31 | this.index.indexField(this.classEntry, access, name, desc, signature); | 30 | |
| 32 | } | ||
| 33 | return super.visitField(access, name, desc, signature, value); | 31 | return super.visitField(access, name, desc, signature, value); |
| 34 | } | 32 | } |
| 35 | 33 | ||
| 36 | @Override | 34 | @Override |
| 37 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | 35 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
| 38 | if (this.classEntry != null) { | 36 | indexer.indexMethod(MethodDefEntry.parse(classEntry, access, name, desc, signature)); |
| 39 | this.index.indexMethod(this.classEntry, access, name, desc, signature); | 37 | |
| 40 | } | ||
| 41 | return super.visitMethod(access, name, desc, signature, exceptions); | 38 | return super.visitMethod(access, name, desc, signature, exceptions); |
| 42 | } | 39 | } |
| 43 | } | 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 | } | ||