From 00fcd0550fcdda621c2e4662f6ddd55ce673b931 Mon Sep 17 00:00:00 2001 From: Gegy Date: Thu, 24 Jan 2019 14:48:32 +0200 Subject: [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 --- src/main/java/cuchaz/enigma/analysis/Access.java | 2 +- .../analysis/ClassImplementationsTreeNode.java | 35 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 32 +- .../enigma/analysis/ClassReferenceTreeNode.java | 30 +- .../cuchaz/enigma/analysis/EntryReference.java | 34 +- .../java/cuchaz/enigma/analysis/EntryRenamer.java | 167 ------ .../enigma/analysis/FieldReferenceTreeNode.java | 39 +- .../cuchaz/enigma/analysis/IndexClassVisitor.java | 43 -- .../enigma/analysis/IndexInnerClassVisitor.java | 29 - .../enigma/analysis/IndexReferenceVisitor.java | 77 --- .../cuchaz/enigma/analysis/IndexTreeBuilder.java | 87 +++ src/main/java/cuchaz/enigma/analysis/JarIndex.java | 583 --------------------- .../analysis/MethodImplementationsTreeNode.java | 48 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 46 +- .../enigma/analysis/MethodReferenceTreeNode.java | 67 ++- .../java/cuchaz/enigma/analysis/ParsedJar.java | 10 +- .../cuchaz/enigma/analysis/ReferenceTreeNode.java | 4 +- .../java/cuchaz/enigma/analysis/SourceIndex.java | 53 +- .../enigma/analysis/SourceIndexClassVisitor.java | 15 +- .../enigma/analysis/SourceIndexMethodVisitor.java | 19 +- .../cuchaz/enigma/analysis/SourceIndexVisitor.java | 8 +- .../cuchaz/enigma/analysis/TranslationIndex.java | 275 ---------- .../enigma/analysis/index/BridgeMethodIndex.java | 77 +++ .../cuchaz/enigma/analysis/index/EntryIndex.java | 109 ++++ .../enigma/analysis/index/IndexClassVisitor.java | 40 ++ .../analysis/index/IndexReferenceVisitor.java | 83 +++ .../enigma/analysis/index/InheritanceIndex.java | 97 ++++ .../cuchaz/enigma/analysis/index/JarIndex.java | 165 ++++++ .../cuchaz/enigma/analysis/index/JarIndexer.java | 24 + .../enigma/analysis/index/ReferenceIndex.java | 83 +++ .../enigma/analysis/index/RemappableIndex.java | 9 + 31 files changed, 989 insertions(+), 1401 deletions(-) delete mode 100644 src/main/java/cuchaz/enigma/analysis/EntryRenamer.java delete mode 100644 src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java delete mode 100644 src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java delete mode 100644 src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java create mode 100644 src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java delete mode 100644 src/main/java/cuchaz/enigma/analysis/JarIndex.java delete mode 100644 src/main/java/cuchaz/enigma/analysis/TranslationIndex.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/JarIndex.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java create mode 100644 src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java (limited to 'src/main/java/cuchaz/enigma/analysis') 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 @@ package cuchaz.enigma.analysis; -import cuchaz.enigma.bytecode.AccessFlags; +import cuchaz.enigma.translation.representation.AccessFlags; import java.lang.reflect.Modifier; 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 @@ package cuchaz.enigma.analysis; import com.google.common.collect.Lists; -import cuchaz.enigma.mapping.entry.ClassEntry; -import cuchaz.enigma.mapping.entry.MethodEntry; -import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.analysis.index.InheritanceIndex; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import javax.swing.tree.DefaultMutableTreeNode; +import java.util.Collection; import java.util.List; public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { - - private final Translator deobfuscatingTranslator; + private final Translator translator; private final ClassEntry entry; - public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { - this.deobfuscatingTranslator = deobfuscatingTranslator; + public ClassImplementationsTreeNode(Translator translator, ClassEntry entry) { + this.translator = translator; this.entry = entry; } public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { // is this the node? - if (node.entry.equals(entry.getOwnerClassEntry())) { + if (node.entry.equals(entry.getParent())) { return node; } @@ -49,24 +51,19 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { return this.entry; } - public String getDeobfClassName() { - return this.deobfuscatingTranslator.getTranslatedClass(entry).getClassName(); - } - @Override public String toString() { - String className = getDeobfClassName(); - if (className == null) { - className = this.entry.getClassName(); - } - return className; + return translator.translate(entry).toString(); } public void load(JarIndex index) { // get all method implementations List nodes = Lists.newArrayList(); - for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { - nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName))); + InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); + + Collection inheritors = inheritanceIndex.getChildren(entry); + for (ClassEntry inheritor : inheritors) { + nodes.add(new ClassImplementationsTreeNode(translator, inheritor)); } // 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 @@ package cuchaz.enigma.analysis; import com.google.common.collect.Lists; -import cuchaz.enigma.mapping.entry.ClassEntry; -import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.analysis.index.InheritanceIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.entry.ClassEntry; import javax.swing.tree.DefaultMutableTreeNode; import java.util.List; public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { - - private final Translator deobfuscatingTranslator; + private final Translator translator; private final ClassEntry obfClassEntry; - public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { - this.deobfuscatingTranslator = deobfuscatingTranslator; + public ClassInheritanceTreeNode(Translator translator, String obfClassName) { + this.translator = translator; this.obfClassEntry = new ClassEntry(obfClassName); } public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { // is this the node? - if (node.getObfClassName().equals(entry.getName())) { + if (node.getObfClassName().equals(entry.getFullName())) { return node; } @@ -45,27 +45,19 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { } public String getObfClassName() { - return this.obfClassEntry.getClassName(); - } - - public String getDeobfClassName() { - return this.deobfuscatingTranslator.getTranslatedClass(this.obfClassEntry).getClassName(); + return this.obfClassEntry.getFullName(); } @Override public String toString() { - String deobfClassName = getDeobfClassName(); - if (deobfClassName != null) { - return deobfClassName; - } - return this.obfClassEntry.getName(); + return translator.translate(obfClassEntry).getFullName(); } - public void load(TranslationIndex ancestries, boolean recurse) { + public void load(InheritanceIndex ancestries, boolean recurse) { // get all the child nodes List nodes = Lists.newArrayList(); - for (ClassEntry subclassEntry : ancestries.getSubclass(this.obfClassEntry)) { - nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); + for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) { + nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName())); } // 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 @@ package cuchaz.enigma.analysis; import com.google.common.collect.Sets; -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.Translator; -import cuchaz.enigma.mapping.entry.ClassEntry; -import cuchaz.enigma.mapping.entry.Entry; -import cuchaz.enigma.mapping.entry.MethodDefEntry; -import cuchaz.enigma.mapping.entry.MethodEntry; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.analysis.index.ReferenceIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; @@ -29,7 +29,6 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode private Translator deobfuscatingTranslator; private ClassEntry entry; private EntryReference reference; - private AccessFlags access; public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { this.deobfuscatingTranslator = deobfuscatingTranslator; @@ -37,12 +36,10 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode this.reference = null; } - public ClassReferenceTreeNode(Translator deobfuscatingTranslator, - EntryReference reference, AccessFlags access) { + public ClassReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference) { this.deobfuscatingTranslator = deobfuscatingTranslator; this.entry = reference.entry; this.reference = reference; - this.access = access; } @Override @@ -58,16 +55,17 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode @Override public String toString() { if (this.reference != null) { - return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), - this.access); + return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context)); } - return this.deobfuscatingTranslator.getTranslatedClass(this.entry).getName(); + return this.deobfuscatingTranslator.translate(this.entry).getFullName(); } public void load(JarIndex index, boolean recurse) { + ReferenceIndex referenceIndex = index.getReferenceIndex(); + // get all the child nodes - for (EntryReference reference : index.getMethodsReferencing(this.entry)) { - add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); + for (EntryReference reference : referenceIndex.getReferencesToClass(this.entry)) { + add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference)); } if (recurse && this.children != null) { @@ -76,7 +74,7 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode ClassReferenceTreeNode node = (ClassReferenceTreeNode) child; // don't recurse into ancestor - Set ancestors = Sets.newHashSet(); + Set> ancestors = Sets.newHashSet(); TreeNode n = node; while (n.getParent() != null) { 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 @@ package cuchaz.enigma.analysis; -import cuchaz.enigma.mapping.entry.ClassEntry; -import cuchaz.enigma.mapping.entry.Entry; -import cuchaz.enigma.mapping.entry.MethodEntry; +import cuchaz.enigma.translation.Translatable; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryResolver; +import cuchaz.enigma.translation.mapping.EntryMap; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.Utils; import java.util.Arrays; import java.util.List; -public class EntryReference { +public class EntryReference, C extends Entry> implements Translatable { private static final List CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); public E entry; @@ -53,32 +58,24 @@ public class EntryReference { public ClassEntry getLocationClassEntry() { if (context != null) { - return context.getOwnerClassEntry(); + return context.getContainingClass(); } - return entry.getOwnerClassEntry(); + return entry.getContainingClass(); } public boolean isNamed() { return this.sourceName; } - public Entry getNameableEntry() { + public Entry getNameableEntry() { if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) { // renaming a constructor really means renaming the class - return entry.getOwnerClassEntry(); + return entry.getContainingClass(); } return entry; } public String getNameableName() { - if (getNameableEntry() instanceof ClassEntry) { - ClassEntry classEntry = (ClassEntry) getNameableEntry(); - if (classEntry.isInnerClass()) { - // make sure we only rename the inner class name - return classEntry.getInnermostClassName(); - } - } - return getNameableEntry().getName(); } @@ -121,4 +118,9 @@ public class EntryReference { } return buf.toString(); } + + @Override + public Translatable translate(Translator translator, EntryResolver resolver, EntryMap mappings) { + return new EntryReference<>(translator.translate(entry), translator.translate(context), this); + } } 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 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.analysis; - -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.mapping.entry.*; - -import java.util.AbstractMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class EntryRenamer { - - public static void renameClassesInSet(Map renames, Set set) { - List entries = Lists.newArrayList(); - for (T val : set) { - entries.add(renameClassesInThing(renames, val)); - } - set.clear(); - set.addAll(entries); - } - - public static void renameClassesInMap(Map renames, Map map) { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - for (Map.Entry entry : map.entrySet()) { - entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); - } - map.clear(); - for (Map.Entry entry : entriesToAdd) { - map.put(entry.getKey(), entry.getValue()); - } - } - - public static void renameClassesInMultimap(Map renames, Multimap map) { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - for (Map.Entry entry : map.entries()) { - entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); - } - map.clear(); - for (Map.Entry entry : entriesToAdd) { - map.put(entry.getKey(), entry.getValue()); - } - } - - public static void renameMethodsInMultimap(Map renames, Multimap map) { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - for (Map.Entry entry : map.entries()) { - entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); - } - map.clear(); - for (Map.Entry entry : entriesToAdd) { - map.put(entry.getKey(), entry.getValue()); - } - } - - public static void renameMethodsInMap(Map renames, Map map) { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - for (Map.Entry entry : map.entrySet()) { - entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); - } - map.clear(); - for (Map.Entry entry : entriesToAdd) { - map.put(entry.getKey(), entry.getValue()); - } - } - - @SuppressWarnings("unchecked") - public static T renameMethodsInThing(Map renames, T thing) { - if (thing instanceof MethodEntry) { - MethodEntry methodEntry = (MethodEntry) thing; - MethodEntry newMethodEntry = renames.get(methodEntry); - if (newMethodEntry != null) { - return (T) new MethodEntry( - methodEntry.getOwnerClassEntry(), - newMethodEntry.getName(), - methodEntry.getDesc() - ); - } - return thing; - } else if (thing instanceof LocalVariableEntry) { - LocalVariableEntry variableEntry = (LocalVariableEntry) thing; - return (T) new LocalVariableEntry( - renameMethodsInThing(renames, variableEntry.getOwnerEntry()), - variableEntry.getIndex(), - variableEntry.getName(), - variableEntry.isParameter() - ); - } else if (thing instanceof EntryReference) { - EntryReference reference = (EntryReference) thing; - reference.entry = renameMethodsInThing(renames, reference.entry); - reference.context = renameMethodsInThing(renames, reference.context); - return thing; - } - return thing; - } - - @SuppressWarnings("unchecked") - public static T renameClassesInThing(final Map renames, T thing) { - if (thing instanceof String) { - String stringEntry = (String) thing; - if (renames.containsKey(stringEntry)) { - return (T) renames.get(stringEntry); - } - } else if (thing instanceof ClassEntry) { - ClassEntry classEntry = (ClassEntry) thing; - return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); - } else if (thing instanceof FieldDefEntry) { - FieldDefEntry fieldEntry = (FieldDefEntry) thing; - return (T) new FieldDefEntry( - renameClassesInThing(renames, fieldEntry.getOwnerClassEntry()), - fieldEntry.getName(), - renameClassesInThing(renames, fieldEntry.getDesc()), - renameClassesInThing(renames, fieldEntry.getSignature()), - fieldEntry.getAccess() - ); - } else if (thing instanceof MethodDefEntry) { - MethodDefEntry methodEntry = (MethodDefEntry) thing; - return (T) new MethodDefEntry( - renameClassesInThing(renames, methodEntry.getOwnerClassEntry()), - methodEntry.getName(), - renameClassesInThing(renames, methodEntry.getDesc()), - renameClassesInThing(renames, methodEntry.getSignature()), - methodEntry.getAccess() - ); - } else if (thing instanceof MethodEntry) { - MethodEntry methodEntry = (MethodEntry) thing; - return (T) new MethodEntry( - renameClassesInThing(renames, methodEntry.getOwnerClassEntry()), - methodEntry.getName(), - renameClassesInThing(renames, methodEntry.getDesc()) - ); - } else if (thing instanceof LocalVariableEntry) { - LocalVariableEntry argumentEntry = (LocalVariableEntry) thing; - return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName(), argumentEntry.isParameter()); - } else if (thing instanceof EntryReference) { - EntryReference reference = (EntryReference) thing; - reference.entry = renameClassesInThing(renames, reference.entry); - reference.context = renameClassesInThing(renames, reference.context); - return thing; - } else if (thing instanceof MethodDescriptor) { - return (T) ((MethodDescriptor) thing).remap(className -> renameClassesInThing(renames, className)); - } else if (thing instanceof TypeDescriptor) { - return (T) ((TypeDescriptor) thing).remap(className -> renameClassesInThing(renames, className)); - } else if (thing instanceof Signature) { - return (T) ((Signature) thing).remap(className -> renameClassesInThing(renames, className)); - } - - return thing; - } -} 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 @@ package cuchaz.enigma.analysis; -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.mapping.entry.FieldEntry; -import cuchaz.enigma.mapping.entry.MethodDefEntry; -import cuchaz.enigma.mapping.entry.MethodEntry; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.analysis.index.ReferenceIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import javax.swing.tree.DefaultMutableTreeNode; public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { - private Translator deobfuscatingTranslator; + private final Translator translator; private FieldEntry entry; private EntryReference reference; - private AccessFlags access; - public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { - this.deobfuscatingTranslator = deobfuscatingTranslator; + public FieldReferenceTreeNode(Translator translator, FieldEntry entry) { + this.translator = translator; this.entry = entry; this.reference = null; } - private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, AccessFlags access) { - this.deobfuscatingTranslator = deobfuscatingTranslator; + private FieldReferenceTreeNode(Translator translator, EntryReference reference) { + this.translator = translator; this.entry = reference.entry; this.reference = reference; - this.access = access; } @Override @@ -52,27 +51,29 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re @Override public String toString() { if (this.reference != null) { - return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), this.access); + return String.format("%s", translator.translate(this.reference.context)); } - return deobfuscatingTranslator.getTranslatedField(entry).getName(); + return translator.translate(entry).toString(); } public void load(JarIndex index, boolean recurse) { + ReferenceIndex referenceIndex = index.getReferenceIndex(); + // get all the child nodes if (this.reference == null) { - for (EntryReference reference : index.getFieldReferences(this.entry)) { - add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); + for (EntryReference reference : referenceIndex.getReferencesToField(this.entry)) { + add(new FieldReferenceTreeNode(translator, reference)); } } else { - for (EntryReference reference : index.getMethodsReferencing(this.reference.context)) { - add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.reference.context))); + for (EntryReference reference : referenceIndex.getReferencesToMethod(this.reference.context)) { + add(new MethodReferenceTreeNode(translator, reference)); } } if (recurse && children != null) { for (Object node : children) { if (node instanceof MethodReferenceTreeNode) { - ((MethodReferenceTreeNode) node).load(index, true); + ((MethodReferenceTreeNode) node).load(index, true, false); } else if (node instanceof FieldReferenceTreeNode) { ((FieldReferenceTreeNode) node).load(index, true); } diff --git a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java deleted file mode 100644 index 4d5e803..0000000 --- a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java +++ /dev/null @@ -1,43 +0,0 @@ -package cuchaz.enigma.analysis; - -import cuchaz.enigma.mapping.entry.ClassDefEntry; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; - -public class IndexClassVisitor extends ClassVisitor { - private final JarIndex index; - private ClassDefEntry classEntry; - - public IndexClassVisitor(JarIndex index, int api) { - super(api); - this.index = index; - } - - public IndexClassVisitor(JarIndex index, int api, ClassVisitor cv) { - super(api, cv); - this.index = index; - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - this.classEntry = this.index.indexClass(access, name, signature, superName, interfaces); - super.visit(version, access, name, signature, superName, interfaces); - } - - @Override - public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { - if (this.classEntry != null) { - this.index.indexField(this.classEntry, access, name, desc, signature); - } - return super.visitField(access, name, desc, signature, value); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - if (this.classEntry != null) { - this.index.indexMethod(this.classEntry, access, name, desc, signature); - } - return super.visitMethod(access, name, desc, signature, exceptions); - } -} 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 @@ -package cuchaz.enigma.analysis; - -import cuchaz.enigma.mapping.entry.ClassEntry; -import org.objectweb.asm.ClassVisitor; - -public class IndexInnerClassVisitor extends ClassVisitor { - private final JarIndex index; - - public IndexInnerClassVisitor(JarIndex index, int api) { - super(api); - this.index = index; - } - - public IndexInnerClassVisitor(JarIndex index, int api, ClassVisitor cv) { - super(api, cv); - this.index = index; - } - - @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - ClassEntry entry = new ClassEntry(name); - // Ignore anonymous classes - if (innerName != null && outerName != null) { - ClassEntry outerEntry = new ClassEntry(outerName); - index.indexInnerClass(entry, outerEntry); - } - super.visitInnerClass(name, outerName, innerName, access); - } -} 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 @@ -package cuchaz.enigma.analysis; - -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.MethodDescriptor; -import cuchaz.enigma.mapping.Signature; -import cuchaz.enigma.mapping.entry.ClassEntry; -import cuchaz.enigma.mapping.entry.MethodDefEntry; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Handle; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -public class IndexReferenceVisitor extends ClassVisitor { - private final JarIndex index; - private ClassEntry classEntry; - - public IndexReferenceVisitor(JarIndex index, int api) { - super(api); - this.index = index; - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - this.classEntry = new ClassEntry(name); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); - return new Method(this.index, entry, this.api); - } - - private class Method extends MethodVisitor { - private final JarIndex index; - private final MethodDefEntry callerEntry; - - public Method(JarIndex index, MethodDefEntry callerEntry, int api) { - super(api); - this.index = index; - this.callerEntry = callerEntry; - } - - @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - this.index.indexFieldAccess(callerEntry, owner, name, desc); - } - - @Override - public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - this.index.indexMethodCall(callerEntry, owner, name, desc); - } - - @Override - public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { - for (Object bsmArg : bsmArgs){ - if (bsmArg instanceof Handle){ - Handle handle = (Handle)bsmArg; - switch (handle.getTag()){ - case Opcodes.H_GETFIELD: - case Opcodes.H_GETSTATIC: - case Opcodes.H_PUTFIELD: - case Opcodes.H_PUTSTATIC: - this.index.indexFieldAccess(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc()); - break; - case Opcodes.H_INVOKEINTERFACE: - case Opcodes.H_INVOKESPECIAL: - case Opcodes.H_INVOKESTATIC: - case Opcodes.H_INVOKEVIRTUAL: - case Opcodes.H_NEWINVOKESPECIAL: - this.index.indexMethodCall(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc()); - break; - } - } - } - } - } -} 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 @@ +package cuchaz.enigma.analysis; + +import com.google.common.collect.Lists; +import cuchaz.enigma.analysis.index.EntryIndex; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.ResolutionStrategy; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; + +import java.util.List; + +public class IndexTreeBuilder { + private final JarIndex index; + + public IndexTreeBuilder(JarIndex index) { + this.index = index; + } + + public ClassInheritanceTreeNode buildClassInheritance(Translator translator, ClassEntry obfClassEntry) { + // get the root node + List ancestry = Lists.newArrayList(); + ancestry.add(obfClassEntry.getFullName()); + for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) { + ancestry.add(classEntry.getFullName()); + } + + ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(translator, ancestry.get(ancestry.size() - 1)); + + // expand all children recursively + rootNode.load(index.getInheritanceIndex(), true); + + return rootNode; + } + + public ClassImplementationsTreeNode buildClassImplementations(Translator translator, ClassEntry obfClassEntry) { + if (index.getInheritanceIndex().isParent(obfClassEntry)) { + ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(translator, obfClassEntry); + node.load(index); + return node; + } + return null; + } + + public MethodInheritanceTreeNode buildMethodInheritance(Translator translator, MethodEntry obfMethodEntry) { + MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); + + // make a root node at the base + MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( + translator, resolvedEntry, + index.getEntryIndex().hasMethod(resolvedEntry) + ); + + // expand the full tree + rootNode.load(index, true); + + return rootNode; + } + + public List buildMethodImplementations(Translator translator, MethodEntry obfMethodEntry) { + EntryIndex entryIndex = index.getEntryIndex(); + + List ancestorMethodEntries = Lists.newArrayList(); + + if (entryIndex.hasMethod(obfMethodEntry)) { + ancestorMethodEntries.add(obfMethodEntry); + } + + for (ClassEntry ancestorEntry : index.getInheritanceIndex().getAncestors(obfMethodEntry.getParent())) { + MethodEntry ancestorMethod = obfMethodEntry.withParent(ancestorEntry); + if (entryIndex.hasMethod(ancestorMethod)) { + ancestorMethodEntries.add(ancestorMethod); + } + } + + List nodes = Lists.newArrayList(); + if (!ancestorMethodEntries.isEmpty()) { + for (MethodEntry interfaceMethodEntry : ancestorMethodEntries) { + MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, interfaceMethodEntry); + node.load(index); + nodes.add(node); + } + } + + return nodes; + } +} 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 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.analysis; - -import com.google.common.collect.*; -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.mapping.entry.*; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Opcodes; - -import java.util.*; - -public class JarIndex { - - private final ReferencedEntryPool entryPool; - - private Set obfClassEntries; - private TranslationIndex translationIndex; - private Map access; - private Multimap fields; - private Multimap methods; - private Multimap methodImplementations; - private Multimap> methodsReferencing; - private Multimap> methodsReferencingClasses; - private Multimap methodReferences; - private Multimap> fieldReferences; - private Multimap innerClassesByOuter; - private Map outerClassesByInner; - private Map bridgedMethods; - private Set syntheticMethods; - - public JarIndex(ReferencedEntryPool entryPool) { - this.entryPool = entryPool; - this.obfClassEntries = Sets.newHashSet(); - this.translationIndex = new TranslationIndex(entryPool); - this.access = Maps.newHashMap(); - this.fields = HashMultimap.create(); - this.methods = HashMultimap.create(); - this.methodImplementations = HashMultimap.create(); - this.methodsReferencingClasses = HashMultimap.create(); - this.methodsReferencing = HashMultimap.create(); - this.methodReferences = HashMultimap.create(); - this.fieldReferences = HashMultimap.create(); - this.innerClassesByOuter = HashMultimap.create(); - this.outerClassesByInner = Maps.newHashMap(); - this.bridgedMethods = Maps.newHashMap(); - this.syntheticMethods = Sets.newHashSet(); - } - - public void indexJar(ParsedJar jar, boolean buildInnerClasses) { - - // step 1: read the class names - obfClassEntries.addAll(jar.getClassEntries()); - - // step 2: index classes, fields, methods, interfaces - if (buildInnerClasses) { - // + step 5: index inner classes - jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5, new IndexInnerClassVisitor(this, Opcodes.ASM5)), ClassReader.SKIP_CODE); - } else { - jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); - } - - // step 3: index field, method, constructor references - jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES); - - // step 4: index access and bridged methods - for (MethodDefEntry methodEntry : methods.values()) { - // look for access and bridged methods - MethodEntry accessedMethod = findAccessMethod(methodEntry); - if (accessedMethod != null) { - if (isBridgedMethod(accessedMethod, methodEntry)) { - this.bridgedMethods.put(methodEntry, accessedMethod); - } - } - } - - if (buildInnerClasses) { - // step 6: update other indices with inner class info - Map renames = Maps.newHashMap(); - for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) { - String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); - if (!innerClassEntry.getName().equals(newName)) { - // DEBUG - //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); - renames.put(innerClassEntry.getName(), newName); - } - } - EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); - this.translationIndex.renameClasses(renames); - EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); - EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencingClasses); - EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencing); - EntryRenamer.renameClassesInMultimap(renames, this.methodReferences); - EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); - EntryRenamer.renameClassesInMap(renames, this.access); - } - } - - protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) { - for (String interfaceName : interfaces) { - if (name.equals(interfaceName)) { - throw new IllegalArgumentException("Class cannot be its own interface! " + name); - } - } - ClassDefEntry entry = this.translationIndex.indexClass(access, name, signature, superName, interfaces); - this.access.put(entry, entry.getAccess()); - return entry; - } - - protected void indexField(ClassDefEntry owner, int access, String name, String desc, String signature) { - FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access)); - this.translationIndex.indexField(fieldEntry); - this.access.put(fieldEntry, fieldEntry.getAccess()); - this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry); - } - - protected void indexMethod(ClassDefEntry owner, int access, String name, String desc, String signature) { - MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); - this.translationIndex.indexMethod(methodEntry); - this.access.put(methodEntry, methodEntry.getAccess()); - this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry); - - if (new AccessFlags(access).isSynthetic()) { - syntheticMethods.add(methodEntry); - } - - // we don't care about constructors here - if (!methodEntry.isConstructor()) { - // index implementation - this.methodImplementations.put(methodEntry.getClassName(), methodEntry); - } - } - - protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) { - ClassEntry referencedClass = entryPool.getClass(owner); - MethodEntry referencedMethod = new MethodEntry(referencedClass, name, new MethodDescriptor(desc)); - ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod); - if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) { - referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry); - } - methodsReferencing.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry)); - if (referencedMethod.isConstructor()) { - methodsReferencingClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedMethod.getName(), callerEntry)); - } - methodReferences.put(callerEntry, referencedMethod); - } - - protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) { - FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc)); - ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField); - if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) { - referencedField = referencedField.updateOwnership(resolvedClassEntry); - } - fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry)); - } - - public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) { - this.innerClassesByOuter.put(outerEntry, innerEntry); - this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry); - } - - private MethodEntry findAccessMethod(MethodDefEntry method) { - - // we want to find all compiler-added methods that directly call another with no processing - - // skip non-synthetic methods - if (!method.getAccess().isSynthetic()) { - return null; - } - - // get all the methods that we call - final Collection referencedMethods = methodReferences.get(method); - - // is there just one? - if (referencedMethods.size() != 1) { - return null; - } - - return referencedMethods.stream().findFirst().orElse(null); - } - - private boolean isBridgedMethod(MethodEntry called, MethodEntry access) { - // Bridged methods will always have the same name as the method they are calling - // They will also have the same amount of parameters (though equal descriptors cannot be guaranteed) - if (!called.getName().equals(access.getName()) || called.getDesc().getArgumentDescs().size() != access.getDesc().getArgumentDescs().size()) { - return false; - } - - TypeDescriptor accessReturn = access.getDesc().getReturnDesc(); - TypeDescriptor calledReturn = called.getDesc().getReturnDesc(); - if (calledReturn.isVoid() || calledReturn.isPrimitive() || accessReturn.isVoid() || accessReturn.isPrimitive()) { - return false; - } - - // Bridged methods will never have the same type as what they are calling - if (accessReturn.equals(calledReturn)) { - return false; - } - - String accessType = accessReturn.toString(); - - // If we're casting down from generic type to type-erased Object we're a bridge method - if (accessType.equals("Ljava/lang/Object;")) { - return true; - } - - // Now we need to detect cases where we are being casted down to a higher type bound - List calledAncestry = translationIndex.getAncestry(calledReturn.getTypeEntry()); - return calledAncestry.contains(accessReturn.getTypeEntry()); - } - - public Set getObfClassEntries() { - return this.obfClassEntries; - } - - public Collection getObfFieldEntries() { - return this.fields.values(); - } - - public Collection getObfFieldEntries(ClassEntry classEntry) { - return this.fields.get(classEntry); - } - - public Collection getObfBehaviorEntries() { - return this.methods.values(); - } - - public Collection getObfBehaviorEntries(ClassEntry classEntry) { - return this.methods.get(classEntry); - } - - public TranslationIndex getTranslationIndex() { - return this.translationIndex; - } - - @Deprecated - public Access getAccess(Entry entry) { - AccessFlags flags = getAccessFlags(entry); - return flags != null ? Access.get(flags) : null; - } - - public AccessFlags getAccessFlags(Entry entry) { - return this.access.get(entry); - } - - public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { - - // get the root node - List ancestry = Lists.newArrayList(); - ancestry.add(obfClassEntry.getName()); - for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) { - if (containsObfClass(classEntry)) { - ancestry.add(classEntry.getName()); - } - } - ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( - deobfuscatingTranslator, - ancestry.get(ancestry.size() - 1) - ); - - // expand all children recursively - rootNode.load(this.translationIndex, true); - - return rootNode; - } - - public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { - - // is this even an interface? - if (isInterface(obfClassEntry.getClassName())) { - ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); - node.load(this); - return node; - } - return null; - } - - public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { - // travel to the ancestor implementation - LinkedList entries = new LinkedList<>(); - entries.add(obfMethodEntry.getOwnerClassEntry()); - - // TODO: This could be optimized to not go through interfaces repeatedly... - - ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry(); - - for (ClassEntry itf : getInterfaces(obfMethodEntry.getOwnerClassEntry().getClassName())) { - MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); - if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) { - baseImplementationClassEntry = itf; - } - } - - for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(entries.remove())) { - MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); - if (ancestorMethodEntry != null) { - if (containsObfMethod(ancestorMethodEntry)) { - baseImplementationClassEntry = ancestorClassEntry; - } - - for (ClassEntry itf : getInterfaces(ancestorClassEntry.getClassName())) { - MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); - if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) { - baseImplementationClassEntry = itf; - } - } - } - } - - // make a root node at the base - MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); - MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( - deobfuscatingTranslator, - methodEntry, - containsObfMethod(methodEntry) - ); - - // expand the full tree - rootNode.load(this, true); - - return rootNode; - } - - public List getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { - - List interfaceMethodEntries = Lists.newArrayList(); - - // is this method on an interface? - if (isInterface(obfMethodEntry.getClassName())) { - interfaceMethodEntries.add(obfMethodEntry); - } else { - // get the interface class - for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { - - // is this method defined in this interface? - MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); - if (methodInterface != null && containsObfMethod(methodInterface)) { - interfaceMethodEntries.add(methodInterface); - } - } - } - - List nodes = Lists.newArrayList(); - if (!interfaceMethodEntries.isEmpty()) { - for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { - MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); - node.load(this); - nodes.add(node); - } - } - return nodes; - } - - public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { - AccessFlags flags = getAccessFlags(obfMethodEntry); - if (flags.isPrivate() || flags.isStatic()) { - return Collections.singleton(obfMethodEntry); - } - - Set methodEntries = Sets.newHashSet(); - getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry)); - return methodEntries; - } - - private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { - MethodEntry methodEntry = node.getMethodEntry(); - if (methodEntries.contains(methodEntry)) { - return; - } - - if (containsObfMethod(methodEntry)) { - AccessFlags flags = getAccessFlags(methodEntry); - if (!flags.isPrivate() && !flags.isStatic()) { - // collect the entry - methodEntries.add(methodEntry); - } - } - - // look at bridge methods! - MethodEntry bridgedMethod = getBridgedMethod(methodEntry); - while (bridgedMethod != null) { - methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod)); - bridgedMethod = getBridgedMethod(bridgedMethod); - } - - // look at interface methods too - for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) { - getRelatedMethodImplementations(methodEntries, implementationsNode); - } - - // recurse - for (int i = 0; i < node.getChildCount(); i++) { - getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i)); - } - } - - private void getRelatedMethodImplementations(Set methodEntries, MethodImplementationsTreeNode node) { - MethodEntry methodEntry = node.getMethodEntry(); - if (containsObfMethod(methodEntry)) { - AccessFlags flags = getAccessFlags(methodEntry); - if (!flags.isPrivate() && !flags.isStatic()) { - // collect the entry - methodEntries.add(methodEntry); - } - } - - // look at bridge methods! - MethodEntry bridgedMethod = getBridgedMethod(methodEntry); - while (bridgedMethod != null) { - methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod)); - bridgedMethod = getBridgedMethod(bridgedMethod); - } - - // recurse - for (int i = 0; i < node.getChildCount(); i++) { - getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i)); - } - } - - public Collection> getFieldReferences(FieldEntry fieldEntry) { - return this.fieldReferences.get(fieldEntry); - } - - public Collection getReferencedFields(MethodDefEntry methodEntry) { - // linear search is fast enough for now - Set fieldEntries = Sets.newHashSet(); - for (EntryReference reference : this.fieldReferences.values()) { - if (reference.context == methodEntry) { - fieldEntries.add(reference.entry); - } - } - return fieldEntries; - } - - public Collection> getMethodsReferencing(ClassEntry classEntry) { - return this.methodsReferencingClasses.get(classEntry); - } - - @Deprecated - public Collection> getMethodsReferencing(MethodEntry methodEntry) { - return getMethodsReferencing(methodEntry, false); - } - - public Collection> getMethodsReferencing(MethodEntry methodEntry, boolean recurse) { - if (!recurse) { - return this.methodsReferencing.get(methodEntry); - } - - List> references = new ArrayList<>(); - Set methodEntries = getRelatedMethodImplementations(methodEntry); - for (MethodEntry entry : methodEntries) { - references.addAll(getMethodsReferencing(entry, false)); - } - return references; - } - - public Collection getReferencedMethods(MethodDefEntry methodEntry) { - return this.methodReferences.get(methodEntry); - } - - public Collection getInnerClasses(ClassEntry obfOuterClassEntry) { - return this.innerClassesByOuter.get(obfOuterClassEntry); - } - - public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { - return this.outerClassesByInner.get(obfInnerClassEntry); - } - - public boolean isSyntheticMethod(MethodEntry methodEntry) { - return this.syntheticMethods.contains(methodEntry); - } - - public Set getInterfaces(String className) { - ClassEntry classEntry = entryPool.getClass(className); - Set interfaces = new HashSet<>(this.translationIndex.getInterfaces(classEntry)); - for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { - interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); - } - return interfaces; - } - - public Set getImplementingClasses(String targetInterfaceName) { - - // linear search is fast enough for now - Set classNames = Sets.newHashSet(); - for (Map.Entry entry : this.translationIndex.getClassInterfaces()) { - ClassEntry classEntry = entry.getKey(); - ClassEntry interfaceEntry = entry.getValue(); - if (interfaceEntry.getName().equals(targetInterfaceName)) { - String className = classEntry.getClassName(); - classNames.add(className); - if (isInterface(className)) { - classNames.addAll(getImplementingClasses(className)); - } - - this.translationIndex.getSubclassNamesRecursively(classNames, classEntry); - } - } - return classNames; - } - - public boolean isInterface(String className) { - return this.translationIndex.isInterface(entryPool.getClass(className)); - } - - public boolean containsObfClass(ClassEntry obfClassEntry) { - return this.obfClassEntries.contains(obfClassEntry); - } - - public boolean containsObfField(FieldEntry obfFieldEntry) { - return this.access.containsKey(obfFieldEntry); - } - - public boolean containsObfMethod(MethodEntry obfMethodEntry) { - return this.access.containsKey(obfMethodEntry); - } - - public boolean containsEntryWithSameName(Entry entry) { - for (Entry target : this.access.keySet()) - if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass())) - return true; - return false; - } - - public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) { - // check the behavior - if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) { - return false; - } - - return true; - } - - public boolean containsObfEntry(Entry obfEntry) { - if (obfEntry instanceof ClassEntry) { - return containsObfClass((ClassEntry) obfEntry); - } else if (obfEntry instanceof FieldEntry) { - return containsObfField((FieldEntry) obfEntry); - } else if (obfEntry instanceof MethodEntry) { - return containsObfMethod((MethodEntry) obfEntry); - } else if (obfEntry instanceof LocalVariableEntry) { - return containsObfVariable((LocalVariableEntry) obfEntry); - } else { - throw new Error("Entry desc not supported: " + obfEntry.getClass().getName()); - } - } - - public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { - return this.bridgedMethods.get(bridgeMethodEntry); - } - - public List getObfClassChain(ClassEntry obfClassEntry) { - - // build class chain in inner-to-outer order - List obfClassChain = Lists.newArrayList(obfClassEntry); - ClassEntry checkClassEntry = obfClassEntry; - while (true) { - ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); - if (obfOuterClassEntry != null) { - obfClassChain.add(obfOuterClassEntry); - checkClassEntry = obfOuterClassEntry; - } else { - break; - } - } - - // switch to outer-to-inner order - Collections.reverse(obfClassChain); - - return obfClassChain; - } -} 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 @@ package cuchaz.enigma.analysis; import com.google.common.collect.Lists; -import cuchaz.enigma.mapping.entry.ClassEntry; -import cuchaz.enigma.mapping.entry.MethodEntry; -import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.analysis.index.EntryIndex; +import cuchaz.enigma.analysis.index.InheritanceIndex; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import javax.swing.tree.DefaultMutableTreeNode; +import java.util.Collection; import java.util.List; public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { - private Translator deobfuscatingTranslator; + private final Translator translator; private MethodEntry entry; - public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { + public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) { + this.translator = translator; if (entry == null) { throw new IllegalArgumentException("Entry cannot be null!"); } - this.deobfuscatingTranslator = deobfuscatingTranslator; this.entry = entry; } @@ -53,35 +57,25 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { return this.entry; } - public String getDeobfClassName() { - return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getClassName(); - } - - public String getDeobfMethodName() { - return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); - } - @Override public String toString() { - String className = getDeobfClassName(); - if (className == null) { - className = this.entry.getClassName(); - } - - String methodName = getDeobfMethodName(); - if (methodName == null) { - methodName = this.entry.getName(); - } + MethodEntry translatedEntry = translator.translate(entry); + String className = translatedEntry.getParent().getFullName(); + String methodName = translatedEntry.getName(); return className + "." + methodName + "()"; } public void load(JarIndex index) { // get all method implementations List nodes = Lists.newArrayList(); - for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { - MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getDesc()); - if (index.containsObfMethod(methodEntry)) { - nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); + EntryIndex entryIndex = index.getEntryIndex(); + InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); + + Collection inheritors = inheritanceIndex.getChildren(entry.getParent()); + for (ClassEntry inheritor : inheritors) { + MethodEntry methodEntry = entry.withParent(inheritor); + if (entryIndex.hasMethod(methodEntry)) { + nodes.add(new MethodImplementationsTreeNode(translator, methodEntry)); } } 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 @@ package cuchaz.enigma.analysis; import com.google.common.collect.Lists; -import cuchaz.enigma.mapping.entry.ClassEntry; -import cuchaz.enigma.mapping.entry.MethodEntry; -import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.analysis.index.EntryIndex; +import cuchaz.enigma.analysis.index.InheritanceIndex; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import javax.swing.tree.DefaultMutableTreeNode; import java.util.List; public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { - private Translator deobfuscatingTranslator; + private final Translator translator; private MethodEntry entry; private boolean isImplemented; - public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { - this.deobfuscatingTranslator = deobfuscatingTranslator; + public MethodInheritanceTreeNode(Translator translator, MethodEntry entry, boolean isImplemented) { + this.translator = translator; this.entry = entry; this.isImplemented = isImplemented; } @@ -51,32 +54,19 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { return this.entry; } - public String getDeobfClassName() { - return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getName(); - } - - public String getDeobfMethodName() { - return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); - } - public boolean isImplemented() { return this.isImplemented; } @Override public String toString() { - String className = getDeobfClassName(); - if (className == null) { - className = this.entry.getClassName(); - } + MethodEntry translatedEntry = translator.translate(entry); + String className = translatedEntry.getContainingClass().getFullName(); if (!this.isImplemented) { return className; } else { - String methodName = getDeobfMethodName(); - if (methodName == null) { - methodName = this.entry.getName(); - } + String methodName = translatedEntry.getName(); return className + "." + methodName + "()"; } } @@ -84,14 +74,12 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { public void load(JarIndex index, boolean recurse) { // get all the child nodes List nodes = Lists.newArrayList(); - for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getOwnerClassEntry())) { - MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); - nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry))); - } + EntryIndex entryIndex = index.getEntryIndex(); + InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); - for (ClassEntry subclassEntry : index.getTranslationIndex().getImplementers(this.entry.getOwnerClassEntry())) { - MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); - nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry))); + for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) { + MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc()); + nodes.add(new MethodInheritanceTreeNode(translator, methodEntry, entryIndex.hasMethod(methodEntry))); } // 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 @@ package cuchaz.enigma.analysis; import com.google.common.collect.Sets; -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.mapping.entry.Entry; -import cuchaz.enigma.mapping.entry.MethodDefEntry; -import cuchaz.enigma.mapping.entry.MethodEntry; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.analysis.index.ReferenceIndex; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.EntryResolver; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; +import java.util.ArrayList; +import java.util.Collection; import java.util.Set; -public class MethodReferenceTreeNode extends DefaultMutableTreeNode - implements ReferenceTreeNode { +public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { - private Translator deobfuscatingTranslator; + private final Translator translator; private MethodEntry entry; private EntryReference reference; - private AccessFlags access; - public MethodReferenceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { - this.deobfuscatingTranslator = deobfuscatingTranslator; + public MethodReferenceTreeNode(Translator translator, MethodEntry entry) { + this.translator = translator; this.entry = entry; this.reference = null; } - public MethodReferenceTreeNode(Translator deobfuscatingTranslator, - EntryReference reference, AccessFlags access) { - this.deobfuscatingTranslator = deobfuscatingTranslator; + public MethodReferenceTreeNode(Translator translator, EntryReference reference) { + this.translator = translator; this.entry = reference.entry; this.reference = reference; - this.access = access; } @Override @@ -57,21 +57,17 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode @Override public String toString() { if (this.reference != null) { - return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), - this.access); + return String.format("%s", translator.translate(this.reference.context)); } - return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); - } - - @Deprecated - public void load(JarIndex index, boolean recurse) { - load(index, recurse, false); + return translator.translate(this.entry).getName(); } public void load(JarIndex index, boolean recurse, boolean recurseMethod) { // get all the child nodes - for (EntryReference reference : index.getMethodsReferencing(this.entry, recurseMethod)) { - add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); + Collection> references = getReferences(index, recurseMethod); + + for (EntryReference reference : references) { + add(new MethodReferenceTreeNode(translator, reference)); } if (recurse && this.children != null) { @@ -80,7 +76,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode MethodReferenceTreeNode node = (MethodReferenceTreeNode) child; // don't recurse into ancestor - Set ancestors = Sets.newHashSet(); + Set> ancestors = Sets.newHashSet(); TreeNode n = node; while (n.getParent() != null) { n = n.getParent(); @@ -92,9 +88,26 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode continue; } - node.load(index, true); + node.load(index, true, false); } } } } + + private Collection> getReferences(JarIndex index, boolean recurseMethod) { + ReferenceIndex referenceIndex = index.getReferenceIndex(); + + if (recurseMethod) { + Collection> references = new ArrayList<>(); + + EntryResolver entryResolver = index.getEntryResolver(); + for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) { + references.addAll(referenceIndex.getReferencesToMethod(methodEntry)); + } + + return references; + } else { + return referenceIndex.getReferencesToMethod(entry); + } + } } 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 @@ package cuchaz.enigma.analysis; import com.google.common.io.ByteStreams; -import cuchaz.enigma.mapping.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.tree.ClassNode; +import javax.annotation.Nullable; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -100,9 +101,14 @@ public class ParsedJar { return entries; } + @Nullable public ClassNode getClassNode(String name) { return nodeCache.computeIfAbsent(name, (n) -> { - ClassReader reader = new ClassReader(classBytes.get(name)); + byte[] bytes = classBytes.get(name); + if (bytes == null) { + return null; + } + ClassReader reader = new ClassReader(bytes); ClassNode node = new ClassNode(); reader.accept(node, 0); 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 @@ package cuchaz.enigma.analysis; -import cuchaz.enigma.mapping.entry.Entry; +import cuchaz.enigma.translation.representation.entry.Entry; -public interface ReferenceTreeNode { +public interface ReferenceTreeNode, C extends Entry> { E getEntry(); EntryReference 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 @@ package cuchaz.enigma.analysis; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; +import com.google.common.collect.*; import com.strobel.decompiler.languages.Region; import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.Identifier; import com.strobel.decompiler.languages.java.ast.TypeDeclaration; -import cuchaz.enigma.mapping.entry.Entry; +import cuchaz.enigma.translation.representation.entry.Entry; +import javax.annotation.Nullable; import java.util.*; import java.util.regex.Pattern; @@ -29,9 +27,9 @@ public class SourceIndex { private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); private String source; - private TreeMap> tokenToReference; - private Multimap, Token> referenceToTokens; - private Map declarationToToken; + private TreeMap, Entry>> tokenToReference; + private Multimap, Entry>, Token> referenceToTokens; + private Map, Token> declarationToToken; private List lineOffsets; private boolean ignoreBadTokens; @@ -42,7 +40,7 @@ public class SourceIndex { public SourceIndex(String source, boolean ignoreBadTokens) { this.source = source; this.ignoreBadTokens = ignoreBadTokens; - this.tokenToReference = Maps.newTreeMap(); + this.tokenToReference = new TreeMap<>(); this.referenceToTokens = HashMultimap.create(); this.declarationToToken = Maps.newHashMap(); calculateLineOffsets(); @@ -63,12 +61,12 @@ public class SourceIndex { this.source = source; calculateLineOffsets(); - for (Entry entry : Lists.newArrayList(declarationToToken.keySet())) { + for (Entry entry : Lists.newArrayList(declarationToToken.keySet())) { Token token = declarationToToken.get(entry); declarationToToken.put(entry, tokenMap.getOrDefault(token, token)); } - for (EntryReference ref : referenceToTokens.keySet()) { + for (EntryReference, Entry> ref : referenceToTokens.keySet()) { Collection oldTokens = referenceToTokens.get(ref); List newTokens = new ArrayList<>(oldTokens.size()); @@ -79,7 +77,8 @@ public class SourceIndex { referenceToTokens.replaceValues(ref, newTokens); } - Map> tokenToReferenceCopy = Maps.newHashMap(tokenToReference); + TreeMap, Entry>> tokenToReferenceCopy = new TreeMap<>(tokenToReference); + tokenToReference.clear(); for (Token token : tokenToReferenceCopy.keySet()) { tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token)); @@ -112,9 +111,9 @@ public class SourceIndex { return null; } - if (node instanceof Identifier && name.indexOf('$') >=0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()){ + if (node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()) { TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; - if (type != null){ + if (type != null) { name = type.getName(); token.end = token.start + name.length(); } @@ -133,19 +132,19 @@ public class SourceIndex { return token; } - public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { + public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { Token token = getToken(node); if (token != null) { - EntryReference deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); + EntryReference, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); this.tokenToReference.put(token, deobfReference); this.referenceToTokens.put(deobfReference, token); } } - public void addDeclaration(AstNode node, Entry deobfEntry) { + public void addDeclaration(AstNode node, Entry deobfEntry) { Token token = getToken(node); if (token != null) { - EntryReference reference = new EntryReference<>(deobfEntry, token.text); + EntryReference, Entry> reference = new EntryReference<>(deobfEntry, token.text); this.tokenToReference.put(token, reference); this.referenceToTokens.put(reference, token); this.declarationToToken.put(deobfEntry, token); @@ -160,22 +159,22 @@ public class SourceIndex { return null; } - public Collection getReferenceTokens(EntryReference deobfReference) { + public Collection getReferenceTokens(EntryReference, Entry> deobfReference) { return this.referenceToTokens.get(deobfReference); } - public EntryReference getDeobfReference(Token token) { + @Nullable + public EntryReference, Entry> getDeobfReference(Token token) { if (token == null) { return null; } return this.tokenToReference.get(token); } - public void replaceDeobfReference(Token token, EntryReference newDeobfReference) { - EntryReference oldDeobfReference = this.tokenToReference.get(token); - this.tokenToReference.put(token, newDeobfReference); - Collection tokens = this.referenceToTokens.get(oldDeobfReference); - this.referenceToTokens.removeAll(oldDeobfReference); + public void replaceDeobfReference(Token token, EntryReference, Entry> newDeobfReference) { + EntryReference, Entry> oldDeobfReferences = this.tokenToReference.replace(token, newDeobfReference); + + Collection tokens = this.referenceToTokens.removeAll(oldDeobfReferences); this.referenceToTokens.putAll(newDeobfReference, tokens); } @@ -187,11 +186,11 @@ public class SourceIndex { return this.declarationToToken.values(); } - public Iterable declarations() { + public Iterable> declarations() { return this.declarationToToken.keySet(); } - public Token getDeclarationToken(Entry deobfEntry) { + public Token getDeclarationToken(Entry deobfEntry) { return this.declarationToToken.get(deobfEntry); } 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; import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.languages.TextLocation; import com.strobel.decompiler.languages.java.ast.*; -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.Signature; -import cuchaz.enigma.mapping.entry.*; +import cuchaz.enigma.translation.representation.ProcyonEntryFactory; +import cuchaz.enigma.translation.representation.ReferencedEntryPool; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; public class SourceIndexClassVisitor extends SourceIndexVisitor { private final ReferencedEntryPool entryPool; @@ -37,7 +40,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { // is this this class, or a subtype? TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); - ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); + ClassDefEntry classEntry = ClassDefEntry.parse(def); if (!classEntry.equals(this.classEntry)) { // it's a subtype, recurse index.addDeclaration(node.getNameToken(), classEntry); @@ -68,7 +71,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { tokenNode = node.getModifiers().firstOrNullObject(); } index.addDeclaration(tokenNode, methodEntry); - return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); + return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); } @Override @@ -76,7 +79,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); index.addDeclaration(node.getNameToken(), methodEntry); - return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); + return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); } @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.*; import com.strobel.decompiler.ast.Variable; import com.strobel.decompiler.languages.TextLocation; import com.strobel.decompiler.languages.java.ast.*; -import cuchaz.enigma.mapping.TypeDescriptor; -import cuchaz.enigma.mapping.entry.*; +import cuchaz.enigma.translation.representation.TypeDescriptor; +import cuchaz.enigma.translation.representation.*; +import cuchaz.enigma.translation.representation.entry.*; import java.lang.Error; import java.util.HashMap; @@ -26,19 +27,15 @@ import java.util.Map; public class SourceIndexMethodVisitor extends SourceIndexVisitor { private final ReferencedEntryPool entryPool; - private final ProcyonEntryFactory entryFactory; - private final ClassDefEntry ownerEntry; private final MethodDefEntry methodEntry; private Multimap unmatchedIdentifier = HashMultimap.create(); - private Map identifierEntryCache = new HashMap<>(); + private Map> identifierEntryCache = new HashMap<>(); - public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, ClassDefEntry ownerEntry, MethodDefEntry methodEntry) { + public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry) { super(entryPool); this.entryPool = entryPool; - this.entryFactory = new ProcyonEntryFactory(entryPool); - this.ownerEntry = ownerEntry; this.methodEntry = methodEntry; } @@ -86,7 +83,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); if (fieldEntry == null) { - throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); + throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName()); } index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); } @@ -128,7 +125,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); if (fieldEntry == null) { - throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); + throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName()); } index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); } else @@ -144,7 +141,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { } private void addDeclarationToUnmatched(String key, SourceIndex index) { - Entry entry = identifierEntryCache.get(key); + Entry entry = identifierEntryCache.get(key); // This cannot happened in theory 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; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.languages.java.ast.*; import com.strobel.decompiler.patterns.Pattern; -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.Signature; -import cuchaz.enigma.mapping.entry.ClassDefEntry; -import cuchaz.enigma.mapping.entry.ReferencedEntryPool; +import cuchaz.enigma.translation.representation.ReferencedEntryPool; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; public class SourceIndexVisitor implements IAstVisitor { private final ReferencedEntryPool entryPool; @@ -29,7 +27,7 @@ public class SourceIndexVisitor implements IAstVisitor { @Override public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); - ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); + ClassDefEntry classEntry = ClassDefEntry.parse(def); index.addDeclaration(node.getNameToken(), classEntry); 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 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.analysis; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import cuchaz.enigma.bytecode.AccessFlags; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.mapping.entry.*; - -import java.util.*; - -public class TranslationIndex { - - private final ReferencedEntryPool entryPool; - private Map superclasses; - private Map defEntries = new HashMap<>(); - private Multimap fieldEntries; - private Multimap methodEntries; - private Multimap interfaces; - - public TranslationIndex(ReferencedEntryPool entryPool) { - this.entryPool = entryPool; - this.superclasses = Maps.newHashMap(); - this.fieldEntries = HashMultimap.create(); - this.methodEntries = HashMultimap.create(); - this.interfaces = HashMultimap.create(); - - for (FieldDefEntry entry : fieldEntries.values()) { - defEntries.put(entry, entry); - } - - for (MethodDefEntry entry : methodEntries.values()) { - defEntries.put(entry, entry); - } - } - - public TranslationIndex(TranslationIndex other, Translator translator) { - this.entryPool = other.entryPool; - - // translate the superclasses - this.superclasses = Maps.newHashMap(); - for (Map.Entry mapEntry : other.superclasses.entrySet()) { - this.superclasses.put(translator.getTranslatedClass(mapEntry.getKey()), translator.getTranslatedClass(mapEntry.getValue())); - } - - // translate the interfaces - this.interfaces = HashMultimap.create(); - for (Map.Entry mapEntry : other.interfaces.entries()) { - this.interfaces.put( - translator.getTranslatedClass(mapEntry.getKey()), - translator.getTranslatedClass(mapEntry.getValue()) - ); - } - - // translate the fields - this.fieldEntries = HashMultimap.create(); - for (Map.Entry mapEntry : other.fieldEntries.entries()) { - this.fieldEntries.put( - translator.getTranslatedClass(mapEntry.getKey()), - translator.getTranslatedFieldDef(mapEntry.getValue()) - ); - } - - this.methodEntries = HashMultimap.create(); - for (Map.Entry mapEntry : other.methodEntries.entries()) { - this.methodEntries.put( - translator.getTranslatedClass(mapEntry.getKey()), - translator.getTranslatedMethodDef(mapEntry.getValue()) - ); - } - - for (FieldDefEntry entry : fieldEntries.values()) { - defEntries.put(entry, entry); - } - - for (MethodDefEntry entry : methodEntries.values()) { - defEntries.put(entry, entry); - } - } - - protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) { - ClassDefEntry classEntry = new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access)); - if (isJre(classEntry)) { - return null; - } - - // add the superclass - ClassEntry superclassEntry = entryPool.getClass(superName); - if (superclassEntry != null) { - this.superclasses.put(classEntry, superclassEntry); - } - - // add the interfaces - for (String interfaceClassName : interfaces) { - ClassEntry interfaceClassEntry = entryPool.getClass(interfaceClassName); - if (!isJre(interfaceClassEntry)) { - this.interfaces.put(classEntry, interfaceClassEntry); - } - } - - return classEntry; - } - - protected void indexField(FieldDefEntry fieldEntry) { - this.fieldEntries.put(fieldEntry.getOwnerClassEntry(), fieldEntry); - this.defEntries.put(fieldEntry, fieldEntry); - } - - protected void indexMethod(MethodDefEntry methodEntry) { - this.methodEntries.put(methodEntry.getOwnerClassEntry(), methodEntry); - this.defEntries.put(methodEntry, methodEntry); - } - - public void renameClasses(Map renames) { - EntryRenamer.renameClassesInMap(renames, this.superclasses); - EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); - EntryRenamer.renameClassesInMultimap(renames, this.methodEntries); - - this.defEntries.clear(); - for (FieldDefEntry entry : fieldEntries.values()) { - defEntries.put(entry, entry); - } - - for (MethodDefEntry entry : methodEntries.values()) { - defEntries.put(entry, entry); - } - } - - public ClassEntry getSuperclass(ClassEntry classEntry) { - return this.superclasses.get(classEntry); - } - - public List getAncestry(ClassEntry classEntry) { - List ancestors = Lists.newArrayList(); - while (classEntry != null) { - classEntry = getSuperclass(classEntry); - if (classEntry != null) { - ancestors.add(classEntry); - } - } - return ancestors; - } - - public List getImplementers(ClassEntry classEntry) { - // linear search is fast enough for now - List implementers = Lists.newArrayList(); - for (ClassEntry itf : this.interfaces.keySet()) { - if (this.interfaces.containsEntry(itf, classEntry)) { - implementers.add(itf); - } - } - return implementers; - } - - public List getSubclass(ClassEntry classEntry) { - // linear search is fast enough for now - List subclasses = Lists.newArrayList(); - for (Map.Entry entry : this.superclasses.entrySet()) { - ClassEntry subclass = entry.getKey(); - ClassEntry superclass = entry.getValue(); - if (classEntry.equals(superclass)) { - subclasses.add(subclass); - } - } - return subclasses; - } - - public void getSubclassesRecursively(Set out, ClassEntry classEntry) { - for (ClassEntry subclassEntry : getSubclass(classEntry)) { - out.add(subclassEntry); - getSubclassesRecursively(out, subclassEntry); - } - } - - public void getSubclassNamesRecursively(Set out, ClassEntry classEntry) { - for (ClassEntry subclassEntry : getSubclass(classEntry)) { - out.add(subclassEntry.getName()); - getSubclassNamesRecursively(out, subclassEntry); - } - } - - public Collection> getClassInterfaces() { - return this.interfaces.entries(); - } - - public Collection getInterfaces(ClassEntry classEntry) { - return this.interfaces.get(classEntry); - } - - public boolean isInterface(ClassEntry classEntry) { - return this.interfaces.containsValue(classEntry); - } - - public boolean entryExists(Entry entry) { - if (entry == null) { - return false; - } - - if (entry instanceof FieldEntry) { - return fieldExists((FieldEntry) entry); - } else if (entry instanceof MethodEntry) { - return methodExists((MethodEntry) entry); - } else if (entry instanceof LocalVariableEntry) { - return methodExists(((LocalVariableEntry) entry).getOwnerEntry()); - } - throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); - } - - public boolean fieldExists(FieldEntry fieldEntry) { - return this.fieldEntries.containsEntry(fieldEntry.getOwnerClassEntry(), fieldEntry); - } - - public boolean methodExists(MethodEntry methodEntry) { - return this.methodEntries.containsEntry(methodEntry.getOwnerClassEntry(), methodEntry); - } - - public ClassEntry resolveEntryOwner(Entry entry) { - if (entry instanceof ClassEntry) { - return (ClassEntry) entry; - } - - if (entryExists(entry)) { - return entry.getOwnerClassEntry(); - } - - DefEntry def = defEntries.get(entry); - if (def != null && (def.getAccess().isPrivate())) { - return null; - } - - // if we're protected/public/non-static, chances are we're somewhere down - LinkedList classEntries = new LinkedList<>(); - classEntries.add(entry.getOwnerClassEntry()); - while (!classEntries.isEmpty()) { - ClassEntry c = classEntries.remove(); - Entry cEntry = entry.updateOwnership(c); - - if (entryExists(cEntry)) { - def = defEntries.get(cEntry); - if (def == null || (!def.getAccess().isPrivate())) { - return cEntry.getOwnerClassEntry(); - } - } - - ClassEntry superC = getSuperclass(c); - if (superC != null) { - classEntries.add(superC); - } - if (entry instanceof MethodEntry) { - classEntries.addAll(getInterfaces(c)); - } - } - - return null; - } - - private boolean isJre(ClassEntry classEntry) { - String packageName = classEntry.getPackageName(); - return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); - } -} 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 @@ +package cuchaz.enigma.analysis.index; + +import com.google.common.collect.Maps; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.EntryResolver; +import cuchaz.enigma.translation.representation.AccessFlags; +import cuchaz.enigma.translation.representation.entry.MethodEntry; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Map; + +public class BridgeMethodIndex implements JarIndexer, RemappableIndex { + private final EntryIndex entryIndex; + private final ReferenceIndex referenceIndex; + + private Map accessedToBridge = Maps.newHashMap(); + + public BridgeMethodIndex(EntryIndex entryIndex, ReferenceIndex referenceIndex) { + this.entryIndex = entryIndex; + this.referenceIndex = referenceIndex; + } + + @Override + public void remap(Translator translator) { + accessedToBridge = translator.translate(accessedToBridge); + } + + @Override + public BridgeMethodIndex remapped(Translator translator) { + BridgeMethodIndex index = new BridgeMethodIndex(entryIndex, referenceIndex); + index.accessedToBridge = translator.translate(accessedToBridge); + + return index; + } + + @Override + public void processIndex(EntryResolver resolver) { + // look for access and bridged methods + for (MethodEntry methodEntry : entryIndex.getMethods()) { + AccessFlags access = entryIndex.getMethodAccess(methodEntry); + if (access == null || !access.isSynthetic()) { + continue; + } + + indexSyntheticMethod(methodEntry, access); + } + } + + private void indexSyntheticMethod(MethodEntry syntheticMethod, AccessFlags access) { + if (access.isBridge()) { + MethodEntry accessedMethod = findAccessMethod(syntheticMethod); + if (accessedMethod != null) { + accessedToBridge.put(accessedMethod, syntheticMethod); + } + } + } + + private MethodEntry findAccessMethod(MethodEntry method) { + // we want to find all compiler-added methods that directly call another with no processing + + // get all the methods that we call + final Collection referencedMethods = referenceIndex.getMethodsReferencedBy(method); + + // is there just one? + if (referencedMethods.size() != 1) { + return null; + } + + return referencedMethods.stream().findFirst().orElse(null); + } + + @Nullable + public MethodEntry getBridgeFromAccessed(MethodEntry entry) { + return accessedToBridge.get(entry); + } +} 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 @@ +package cuchaz.enigma.analysis.index; + +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.AccessFlags; +import cuchaz.enigma.translation.representation.entry.*; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class EntryIndex implements JarIndexer, RemappableIndex { + private Map classes = new HashMap<>(); + private Map fields = new HashMap<>(); + private Map methods = new HashMap<>(); + + @Override + public void remap(Translator translator) { + classes = translator.translateKeys(classes); + fields = translator.translateKeys(fields); + methods = translator.translateKeys(methods); + } + + @Override + public EntryIndex remapped(Translator translator) { + EntryIndex index = new EntryIndex(); + index.classes = translator.translateKeys(classes); + index.fields = translator.translateKeys(fields); + index.methods = translator.translateKeys(methods); + + return index; + } + + @Override + public void indexClass(ClassDefEntry classEntry) { + classes.put(classEntry, classEntry.getAccess()); + } + + @Override + public void indexMethod(MethodDefEntry methodEntry) { + methods.put(methodEntry, methodEntry.getAccess()); + } + + @Override + public void indexField(FieldDefEntry fieldEntry) { + fields.put(fieldEntry, fieldEntry.getAccess()); + } + + public boolean hasClass(ClassEntry entry) { + return classes.containsKey(entry); + } + + public boolean hasMethod(MethodEntry entry) { + return methods.containsKey(entry); + } + + public boolean hasField(FieldEntry entry) { + return fields.containsKey(entry); + } + + public boolean hasEntry(Entry entry) { + if (entry instanceof ClassEntry) { + return hasClass((ClassEntry) entry); + } else if (entry instanceof MethodEntry) { + return hasMethod((MethodEntry) entry); + } else if (entry instanceof FieldEntry) { + return hasField((FieldEntry) entry); + } else if (entry instanceof LocalVariableEntry) { + return hasMethod(((LocalVariableEntry) entry).getParent()); + } + + return false; + } + + @Nullable + public AccessFlags getMethodAccess(MethodEntry entry) { + return methods.get(entry); + } + + @Nullable + public AccessFlags getFieldAccess(FieldEntry entry) { + return fields.get(entry); + } + + @Nullable + public AccessFlags getEntryAccess(Entry entry) { + if (entry instanceof MethodEntry) { + return getMethodAccess((MethodEntry) entry); + } else if (entry instanceof FieldEntry) { + return getFieldAccess((FieldEntry) entry); + } else if (entry instanceof LocalVariableEntry) { + return getMethodAccess(((LocalVariableEntry) entry).getParent()); + } + + return null; + } + + public Collection getClasses() { + return classes.keySet(); + } + + public Collection getMethods() { + return methods.keySet(); + } + + public Collection getFields() { + return fields.keySet(); + } +} diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java new file mode 100644 index 0000000..f9cb23c --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java @@ -0,0 +1,40 @@ +package cuchaz.enigma.analysis.index; + +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; + +public class IndexClassVisitor extends ClassVisitor { + private final JarIndexer indexer; + private ClassDefEntry classEntry; + + public IndexClassVisitor(JarIndex indexer, int api) { + super(api); + this.indexer = indexer; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + classEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces); + indexer.indexClass(classEntry); + + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + indexer.indexField(FieldDefEntry.parse(classEntry, access, name, desc, signature)); + + return super.visitField(access, name, desc, signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + indexer.indexMethod(MethodDefEntry.parse(classEntry, access, name, desc, signature)); + + return super.visitMethod(access, name, desc, signature, exceptions); + } +} 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 @@ +package cuchaz.enigma.analysis.index; + +import cuchaz.enigma.translation.representation.AccessFlags; +import cuchaz.enigma.translation.representation.MethodDescriptor; +import cuchaz.enigma.translation.representation.Signature; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class IndexReferenceVisitor extends ClassVisitor { + private final JarIndexer indexer; + private ClassEntry classEntry; + + public IndexReferenceVisitor(JarIndexer indexer, int api) { + super(api); + this.indexer = indexer; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + this.classEntry = new ClassEntry(name); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); + return new Method(this.indexer, entry, this.api); + } + + private static class Method extends MethodVisitor { + private final JarIndexer indexer; + private final MethodDefEntry callerEntry; + + public Method(JarIndexer indexer, MethodDefEntry callerEntry, int api) { + super(api); + this.indexer = indexer; + this.callerEntry = callerEntry; + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + FieldEntry fieldEntry = FieldEntry.parse(owner, name, desc); + this.indexer.indexFieldReference(callerEntry, fieldEntry); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + MethodEntry methodEntry = MethodEntry.parse(owner, name, desc); + this.indexer.indexMethodReference(callerEntry, methodEntry); + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { + for (Object bsmArg : bsmArgs) { + if (bsmArg instanceof Handle) { + Handle handle = (Handle) bsmArg; + switch (handle.getTag()) { + case Opcodes.H_GETFIELD: + case Opcodes.H_GETSTATIC: + case Opcodes.H_PUTFIELD: + case Opcodes.H_PUTSTATIC: + FieldEntry fieldEntry = FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); + this.indexer.indexFieldReference(callerEntry, fieldEntry); + break; + case Opcodes.H_INVOKEINTERFACE: + case Opcodes.H_INVOKESPECIAL: + case Opcodes.H_INVOKESTATIC: + case Opcodes.H_INVOKEVIRTUAL: + case Opcodes.H_NEWINVOKESPECIAL: + MethodEntry methodEntry = MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); + this.indexer.indexMethodReference(callerEntry, methodEntry); + break; + } + } + } + } + } +} 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 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.analysis.index; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.Set; + +public class InheritanceIndex implements JarIndexer, RemappableIndex { + private Multimap classParents = HashMultimap.create(); + private Multimap classChildren = HashMultimap.create(); + + @Override + public void remap(Translator translator) { + classChildren = translator.translate(classChildren); + classParents = translator.translate(classParents); + } + + @Override + public InheritanceIndex remapped(Translator translator) { + InheritanceIndex index = new InheritanceIndex(); + index.classParents = translator.translate(classParents); + index.classChildren = translator.translate(classChildren); + + return index; + } + + @Override + public void indexClass(ClassDefEntry classEntry) { + ClassEntry superClass = classEntry.getSuperClass(); + if (superClass != null) { + indexParent(classEntry, superClass); + } + + for (ClassEntry interfaceEntry : classEntry.getInterfaces()) { + indexParent(classEntry, interfaceEntry); + } + } + + private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) { + if (childEntry.isJre() || parentEntry.isJre()) { + return; + } + classParents.put(childEntry, parentEntry); + classChildren.put(parentEntry, childEntry); + } + + public Collection getParents(ClassEntry classEntry) { + return classParents.get(classEntry); + } + + public Collection getChildren(ClassEntry classEntry) { + return classChildren.get(classEntry); + } + + public Set getAncestors(ClassEntry classEntry) { + Set ancestors = Sets.newHashSet(); + + LinkedList ancestorQueue = new LinkedList<>(); + ancestorQueue.push(classEntry); + + while (!ancestorQueue.isEmpty()) { + ClassEntry ancestor = ancestorQueue.pop(); + Collection parents = getParents(ancestor); + + parents.forEach(ancestorQueue::push); + ancestors.addAll(parents); + } + + return ancestors; + } + + public boolean isParent(ClassEntry classEntry) { + return classChildren.containsKey(classEntry); + } + + public boolean hasParents(ClassEntry classEntry) { + Collection parents = classParents.get(classEntry); + return parents != null && !parents.isEmpty(); + } +} 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 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.analysis.index; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import cuchaz.enigma.analysis.ParsedJar; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.EntryResolver; +import cuchaz.enigma.translation.mapping.IndexEntryResolver; +import cuchaz.enigma.translation.representation.entry.*; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Consumer; + +public class JarIndex implements JarIndexer, RemappableIndex { + private final EntryIndex entryIndex; + private final InheritanceIndex inheritanceIndex; + private final ReferenceIndex referenceIndex; + private final BridgeMethodIndex bridgeMethodIndex; + private final EntryResolver entryResolver; + + private final Collection indexers; + + private final Multimap methodImplementations = HashMultimap.create(); + + public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex) { + this.entryIndex = entryIndex; + this.inheritanceIndex = inheritanceIndex; + this.referenceIndex = referenceIndex; + this.bridgeMethodIndex = bridgeMethodIndex; + this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex); + this.entryResolver = new IndexEntryResolver(this); + } + + public static JarIndex empty() { + EntryIndex entryIndex = new EntryIndex(); + InheritanceIndex inheritanceIndex = new InheritanceIndex(); + ReferenceIndex referenceIndex = new ReferenceIndex(); + BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, referenceIndex); + return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex); + } + + @Override + public void remap(Translator translator) { + entryIndex.remap(translator); + inheritanceIndex.remap(translator); + bridgeMethodIndex.remap(translator); + } + + @Override + public JarIndex remapped(Translator translator) { + EntryIndex entryIndex = this.entryIndex.remapped(translator); + InheritanceIndex inheritanceIndex = this.inheritanceIndex.remapped(translator); + BridgeMethodIndex bridgeMethodIndex = this.bridgeMethodIndex.remapped(translator); + + JarIndex remappedIndex = new JarIndex(entryIndex, inheritanceIndex, this.referenceIndex, bridgeMethodIndex); + remappedIndex.methodImplementations.putAll(methodImplementations); + + return remappedIndex; + } + + public void indexJar(ParsedJar jar, Consumer progress) { + progress.accept("Indexing entries (1/3)"); + jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); + + progress.accept("Indexing entry references (2/3)"); + jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES); + + progress.accept("Processing index (3/3)"); + processIndex(entryResolver); + } + + @Override + public void processIndex(EntryResolver resolver) { + indexers.forEach(indexer -> indexer.processIndex(entryResolver)); + } + + @Override + public void indexClass(ClassDefEntry classEntry) { + if (classEntry.isJre()) { + return; + } + + for (ClassEntry interfaceEntry : classEntry.getInterfaces()) { + if (classEntry.equals(interfaceEntry)) { + throw new IllegalArgumentException("Class cannot be its own interface! " + classEntry); + } + } + + indexers.forEach(indexer -> indexer.indexClass(classEntry)); + } + + @Override + public void indexField(FieldDefEntry fieldEntry) { + if (fieldEntry.getParent().isJre()) { + return; + } + + indexers.forEach(indexer -> indexer.indexField(fieldEntry)); + } + + @Override + public void indexMethod(MethodDefEntry methodEntry) { + if (methodEntry.getParent().isJre()) { + return; + } + + indexers.forEach(indexer -> indexer.indexMethod(methodEntry)); + + if (!methodEntry.isConstructor()) { + methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry); + } + } + + @Override + public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { + if (callerEntry.getParent().isJre()) { + return; + } + + indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry)); + } + + @Override + public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { + if (callerEntry.getParent().isJre()) { + return; + } + + indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry)); + } + + public EntryIndex getEntryIndex() { + return entryIndex; + } + + public InheritanceIndex getInheritanceIndex() { + return this.inheritanceIndex; + } + + public ReferenceIndex getReferenceIndex() { + return referenceIndex; + } + + public BridgeMethodIndex getBridgeMethodIndex() { + return bridgeMethodIndex; + } + + public EntryResolver getEntryResolver() { + return entryResolver; + } +} 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 @@ +package cuchaz.enigma.analysis.index; + +import cuchaz.enigma.translation.mapping.EntryResolver; +import cuchaz.enigma.translation.representation.entry.*; + +public interface JarIndexer { + default void indexClass(ClassDefEntry classEntry) { + } + + default void indexField(FieldDefEntry fieldEntry) { + } + + default void indexMethod(MethodDefEntry methodEntry) { + } + + default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { + } + + default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { + } + + default void processIndex(EntryResolver resolver) { + } +} 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 @@ +package cuchaz.enigma.analysis.index; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.translation.mapping.EntryResolver; +import cuchaz.enigma.translation.mapping.ResolutionStrategy; +import cuchaz.enigma.translation.representation.entry.*; + +import java.util.Collection; +import java.util.Map; + +public class ReferenceIndex implements JarIndexer { + private Multimap methodReferences = HashMultimap.create(); + + private Multimap> referencesToMethods = HashMultimap.create(); + private Multimap> referencesToClasses = HashMultimap.create(); + private Multimap> referencesToFields = HashMultimap.create(); + + @Override + public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) { + referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry)); + methodReferences.put(callerEntry, referencedEntry); + + if (referencedEntry.isConstructor()) { + ClassEntry referencedClass = referencedEntry.getParent(); + referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry)); + } + } + + @Override + public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) { + referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry)); + } + + @Override + public void processIndex(EntryResolver resolver) { + methodReferences = resolveReferences(resolver, methodReferences); + referencesToMethods = resolveReferencesTo(resolver, referencesToMethods); + referencesToClasses = resolveReferencesTo(resolver, referencesToClasses); + referencesToFields = resolveReferencesTo(resolver, referencesToFields); + } + + private , V extends Entry> Multimap resolveReferences(EntryResolver resolver, Multimap multimap) { + Multimap resolved = HashMultimap.create(); + for (Map.Entry entry : multimap.entries()) { + resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue())); + } + return resolved; + } + + private , C extends Entry> Multimap> resolveReferencesTo(EntryResolver resolver, Multimap> multimap) { + Multimap> resolved = HashMultimap.create(); + for (Map.Entry> entry : multimap.entries()) { + resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue())); + } + return resolved; + } + + private > E resolve(EntryResolver resolver, E entry) { + return resolver.resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST); + } + + private , C extends Entry> EntryReference resolve(EntryResolver resolver, EntryReference reference) { + return resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); + } + + public Collection getMethodsReferencedBy(MethodEntry entry) { + return methodReferences.get(entry); + } + + public Collection> getReferencesToField(FieldEntry entry) { + return referencesToFields.get(entry); + } + + public Collection> getReferencesToClass(ClassEntry entry) { + return referencesToClasses.get(entry); + } + + public Collection> getReferencesToMethod(MethodEntry entry) { + return referencesToMethods.get(entry); + } +} 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 @@ +package cuchaz.enigma.analysis.index; + +import cuchaz.enigma.translation.Translator; + +public interface RemappableIndex { + void remap(Translator translator); + + RemappableIndex remapped(Translator translator); +} -- cgit v1.2.3