From 6e464ea251cab63c776ece0b2a356f1498ffa294 Mon Sep 17 00:00:00 2001 From: Thog Date: Wed, 8 Mar 2017 08:17:04 +0100 Subject: Follow Fabric guidelines --- src/main/java/cuchaz/enigma/analysis/Access.java | 59 +- .../enigma/analysis/BehaviorReferenceTreeNode.java | 131 +- .../java/cuchaz/enigma/analysis/BridgeMarker.java | 35 +- .../analysis/ClassImplementationsTreeNode.java | 95 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 105 +- .../cuchaz/enigma/analysis/EntryReference.java | 207 +-- .../java/cuchaz/enigma/analysis/EntryRenamer.java | 232 +-- .../enigma/analysis/FieldReferenceTreeNode.java | 107 +- .../cuchaz/enigma/analysis/JarClassIterator.java | 194 +-- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 1615 ++++++++++---------- .../analysis/MethodImplementationsTreeNode.java | 147 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 167 +- .../cuchaz/enigma/analysis/ReferenceTreeNode.java | 5 +- .../java/cuchaz/enigma/analysis/SourceIndex.java | 305 ++-- .../analysis/SourceIndexBehaviorVisitor.java | 358 +++-- .../enigma/analysis/SourceIndexClassVisitor.java | 129 +- .../cuchaz/enigma/analysis/SourceIndexVisitor.java | 722 ++++----- src/main/java/cuchaz/enigma/analysis/Token.java | 80 +- .../cuchaz/enigma/analysis/TranslationIndex.java | 543 ++++--- .../cuchaz/enigma/analysis/TreeDumpVisitor.java | 833 +++++----- 20 files changed, 3025 insertions(+), 3044 deletions(-) (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 b8a7b2c..547d85e 100644 --- a/src/main/java/cuchaz/enigma/analysis/Access.java +++ b/src/main/java/cuchaz/enigma/analysis/Access.java @@ -8,40 +8,41 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ -package cuchaz.enigma.analysis; -import java.lang.reflect.Modifier; +package cuchaz.enigma.analysis; import javassist.CtBehavior; import javassist.CtField; +import java.lang.reflect.Modifier; + public enum Access { - PUBLIC, PROTECTED, PACKAGE, PRIVATE; - - public static Access get(CtBehavior behavior) { - return get(behavior.getModifiers()); - } - - public static Access get(CtField field) { - return get(field.getModifiers()); - } - - public static Access get(int modifiers) { - boolean isPublic = Modifier.isPublic(modifiers); - boolean isProtected = Modifier.isProtected(modifiers); - boolean isPrivate = Modifier.isPrivate(modifiers); - - if (isPublic && !isProtected && !isPrivate) { - return PUBLIC; - } else if (!isPublic && isProtected && !isPrivate) { - return PROTECTED; - } else if (!isPublic && !isProtected && isPrivate) { - return PRIVATE; - } else if (!isPublic && !isProtected && !isPrivate) { - return PACKAGE; - } - // assume public by default - return PUBLIC; - } + PUBLIC, PROTECTED, PACKAGE, PRIVATE; + + public static Access get(CtBehavior behavior) { + return get(behavior.getModifiers()); + } + + public static Access get(CtField field) { + return get(field.getModifiers()); + } + + public static Access get(int modifiers) { + boolean isPublic = Modifier.isPublic(modifiers); + boolean isProtected = Modifier.isProtected(modifiers); + boolean isPrivate = Modifier.isPrivate(modifiers); + + if (isPublic && !isProtected && !isPrivate) { + return PUBLIC; + } else if (!isPublic && isProtected && !isPrivate) { + return PROTECTED; + } else if (!isPublic && !isProtected && isPrivate) { + return PRIVATE; + } else if (!isPublic && !isProtected && !isPrivate) { + return PACKAGE; + } + // assume public by default + return PUBLIC; + } } diff --git a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 52d5b31..6556b2c 100644 --- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.Sets; @@ -20,85 +21,73 @@ import javax.swing.tree.TreeNode; import java.util.Set; public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode - implements ReferenceTreeNode -{ + implements ReferenceTreeNode { - private Translator deobfuscatingTranslator; - private BehaviorEntry entry; - private EntryReference reference; - private Access access; + private Translator deobfuscatingTranslator; + private BehaviorEntry entry; + private EntryReference reference; + private Access access; - public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) - { - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.entry = entry; - this.reference = null; - } + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.entry = entry; + this.reference = null; + } - public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, - EntryReference reference, Access access) - { - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.entry = reference.entry; - this.reference = reference; - this.access = access; - } + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, + EntryReference reference, Access access) { + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.entry = reference.entry; + this.reference = reference; + this.access = access; + } - @Override public BehaviorEntry getEntry() - { - return this.entry; - } + @Override + public BehaviorEntry getEntry() { + return this.entry; + } - @Override public EntryReference getReference() - { - return this.reference; - } + @Override + public EntryReference getReference() { + return this.reference; + } - @Override public String toString() - { - if (this.reference != null) - { - return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), - this.access); - } - return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); - } + @Override + public String toString() { + if (this.reference != null) { + return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), + this.access); + } + return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); + } - public void load(JarIndex index, boolean recurse) - { - // get all the child nodes - for (EntryReference reference : index.getBehaviorReferences(this.entry)) - { - add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); - } + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + for (EntryReference reference : index.getBehaviorReferences(this.entry)) { + add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); + } - if (recurse && this.children != null) - { - for (Object child : this.children) - { - if (child instanceof BehaviorReferenceTreeNode) - { - BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child; + if (recurse && this.children != null) { + for (Object child : this.children) { + if (child instanceof BehaviorReferenceTreeNode) { + BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child; - // don't recurse into ancestor - Set ancestors = Sets.newHashSet(); - TreeNode n = node; - while (n.getParent() != null) - { - n = n.getParent(); - if (n instanceof BehaviorReferenceTreeNode) - { - ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); - } - } - if (ancestors.contains(node.getEntry())) - { - continue; - } + // don't recurse into ancestor + Set ancestors = Sets.newHashSet(); + TreeNode n = node; + while (n.getParent() != null) { + n = n.getParent(); + if (n instanceof BehaviorReferenceTreeNode) { + ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); + } + } + if (ancestors.contains(node.getEntry())) { + continue; + } - node.load(index, true); - } - } - } - } + node.load(index, true); + } + } + } + } } diff --git a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java index 0f4be7d..81e750c 100644 --- a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java +++ b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import cuchaz.enigma.mapping.EntryFactory; @@ -18,26 +19,26 @@ import javassist.bytecode.AccessFlag; public class BridgeMarker { - private JarIndex jarIndex; + private JarIndex jarIndex; - public BridgeMarker(JarIndex jarIndex) { - this.jarIndex = jarIndex; - } + public BridgeMarker(JarIndex jarIndex) { + this.jarIndex = jarIndex; + } - public void markBridges(CtClass c) { + public void markBridges(CtClass c) { - for (CtMethod method : c.getDeclaredMethods()) { - MethodEntry methodEntry = EntryFactory.getMethodEntry(method); + for (CtMethod method : c.getDeclaredMethods()) { + MethodEntry methodEntry = EntryFactory.getMethodEntry(method); - // is this a bridge method? - MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry); - if (bridgedMethodEntry != null) { + // is this a bridge method? + MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry); + if (bridgedMethodEntry != null) { - // it's a bridge method! add the bridge flag - int flags = method.getMethodInfo().getAccessFlags(); - flags |= AccessFlag.BRIDGE; - method.getMethodInfo().setAccessFlags(flags); - } - } - } + // it's a bridge method! add the bridge flag + int flags = method.getMethodInfo().getAccessFlags(); + flags |= AccessFlag.BRIDGE; + method.getMethodInfo().setAccessFlags(flags); + } + } + } } diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 70ece24..f2fb2f8 100644 --- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -8,69 +8,68 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.Lists; - -import java.util.List; - -import javax.swing.tree.DefaultMutableTreeNode; - import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; +import javax.swing.tree.DefaultMutableTreeNode; +import java.util.List; + public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { - private Translator deobfuscatingTranslator; - private ClassEntry entry; + private Translator deobfuscatingTranslator; + private ClassEntry entry; - public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.entry = entry; - } + public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.entry = entry; + } - public ClassEntry getClassEntry() { - return this.entry; - } + public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { + // is this the node? + if (node.entry.equals(entry.getClassEntry())) { + return node; + } - public String getDeobfClassName() { - return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); - } + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } - @Override - public String toString() { - String className = getDeobfClassName(); - if (className == null) { - className = this.entry.getClassName(); - } - return className; - } + public ClassEntry getClassEntry() { + return this.entry; + } - 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))); - } + public String getDeobfClassName() { + return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); + } - // add them to this node - nodes.forEach(this::add); - } + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = this.entry.getClassName(); + } + return className; + } - public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { - // is this the node? - if (node.entry.equals(entry.getClassEntry())) { - return node; - } + 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))); + } - // recurse - for (int i = 0; i < node.getChildCount(); i++) { - ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); - if (foundNode != null) { - return foundNode; - } - } - return null; - } + // add them to this node + nodes.forEach(this::add); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 8a60fc7..24e7cb0 100644 --- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -8,74 +8,73 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.Lists; - -import java.util.List; - -import javax.swing.tree.DefaultMutableTreeNode; - import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Translator; +import javax.swing.tree.DefaultMutableTreeNode; +import java.util.List; + public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { - private Translator deobfuscatingTranslator; - private String obfClassName; + private Translator deobfuscatingTranslator; + private String obfClassName; - public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.obfClassName = obfClassName; - } + public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.obfClassName = obfClassName; + } - public String getObfClassName() { - return this.obfClassName; - } + public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { + // is this the node? + if (node.getObfClassName().equals(entry.getName())) { + return node; + } - public String getDeobfClassName() { - return this.deobfuscatingTranslator.translateClass(this.obfClassName); - } + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } - @Override - public String toString() { - String deobfClassName = getDeobfClassName(); - if (deobfClassName != null) { - return deobfClassName; - } - return this.obfClassName; - } + public String getObfClassName() { + return this.obfClassName; + } - public void load(TranslationIndex ancestries, boolean recurse) { - // get all the child nodes - List nodes = Lists.newArrayList(); - for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) { - nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); - } + public String getDeobfClassName() { + return this.deobfuscatingTranslator.translateClass(this.obfClassName); + } - // add them to this node - nodes.forEach(this::add); + @Override + public String toString() { + String deobfClassName = getDeobfClassName(); + if (deobfClassName != null) { + return deobfClassName; + } + return this.obfClassName; + } - if (recurse) { - for (ClassInheritanceTreeNode node : nodes) { - node.load(ancestries, true); - } - } - } + public void load(TranslationIndex ancestries, boolean recurse) { + // get all the child nodes + List nodes = Lists.newArrayList(); + for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) { + nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); + } - public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { - // is this the node? - if (node.getObfClassName().equals(entry.getName())) { - return node; - } + // add them to this node + nodes.forEach(this::add); - // recurse - for (int i = 0; i < node.getChildCount(); i++) { - ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); - if (foundNode != null) { - return foundNode; - } - } - return null; - } + if (recurse) { + for (ClassInheritanceTreeNode node : nodes) { + node.load(ancestries, true); + } + } + } } diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java index ad4baf8..3761fca 100644 --- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java +++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java @@ -8,116 +8,117 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ -package cuchaz.enigma.analysis; -import java.util.Arrays; -import java.util.List; +package cuchaz.enigma.analysis; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.utils.Utils; +import java.util.Arrays; +import java.util.List; + public class EntryReference { - private static final List ConstructorNonNames = Arrays.asList("this", "super", "static"); - public E entry; - public C context; - - private boolean sourceName; - - public EntryReference(E entry, String sourceName) { - this(entry, sourceName, null); - } - - public EntryReference(E entry, String sourceName, C context) { - if (entry == null) { - throw new IllegalArgumentException("Entry cannot be null!"); - } - - this.entry = entry; - this.context = context; - - this.sourceName = sourceName != null && sourceName.length() > 0; - if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { - this.sourceName = false; - } - } - - public EntryReference(E entry, C context, EntryReference other) { - this.entry = entry; - this.context = context; - this.sourceName = other.sourceName; - } - - public ClassEntry getLocationClassEntry() { - if (context != null) { - return context.getClassEntry(); - } - return entry.getClassEntry(); - } - - public boolean isNamed() { - return this.sourceName; - } - - public Entry getNameableEntry() { - if (entry instanceof ConstructorEntry) { - // renaming a constructor really means renaming the class - return entry.getClassEntry(); - } - return entry; - } - - public String getNamableName() { - 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(); - } - - @Override - public int hashCode() { - if (context != null) { - return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode()); - } - return entry.hashCode(); - } - - @Override - public boolean equals(Object other) { - return other instanceof EntryReference && equals((EntryReference) other); - } - - public boolean equals(EntryReference other) { - // check entry first - boolean isEntrySame = entry.equals(other.entry); - if (!isEntrySame) { - return false; - } - - // check caller - if (context == null && other.context == null) { - return true; - } else if (context != null && other.context != null) { - return context.equals(other.context); - } - return false; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append(entry); - if (context != null) { - buf.append(" called from "); - buf.append(context); - } - return buf.toString(); - } + private static final List ConstructorNonNames = Arrays.asList("this", "super", "static"); + public E entry; + public C context; + + private boolean sourceName; + + public EntryReference(E entry, String sourceName) { + this(entry, sourceName, null); + } + + public EntryReference(E entry, String sourceName, C context) { + if (entry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); + } + + this.entry = entry; + this.context = context; + + this.sourceName = sourceName != null && !sourceName.isEmpty(); + if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { + this.sourceName = false; + } + } + + public EntryReference(E entry, C context, EntryReference other) { + this.entry = entry; + this.context = context; + this.sourceName = other.sourceName; + } + + public ClassEntry getLocationClassEntry() { + if (context != null) { + return context.getClassEntry(); + } + return entry.getClassEntry(); + } + + public boolean isNamed() { + return this.sourceName; + } + + public Entry getNameableEntry() { + if (entry instanceof ConstructorEntry) { + // renaming a constructor really means renaming the class + return entry.getClassEntry(); + } + return entry; + } + + public String getNamableName() { + 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(); + } + + @Override + public int hashCode() { + if (context != null) { + return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode()); + } + return entry.hashCode(); + } + + @Override + public boolean equals(Object other) { + return other instanceof EntryReference && equals((EntryReference) other); + } + + public boolean equals(EntryReference other) { + // check entry first + boolean isEntrySame = entry.equals(other.entry); + if (!isEntrySame) { + return false; + } + + // check caller + if (context == null && other.context == null) { + return true; + } else if (context != null && other.context != null) { + return context.equals(other.context); + } + return false; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(entry); + if (context != null) { + buf.append(" called from "); + buf.append(context); + } + return buf.toString(); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java index 7233fcf..75806c3 100644 --- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java @@ -8,140 +8,140 @@ * 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 java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.Set; -import cuchaz.enigma.mapping.*; - 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 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 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 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 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()); - } - } + 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.getClassEntry(), - newMethodEntry.getName(), - methodEntry.getSignature() - ); - } - return thing; - } else if (thing instanceof ArgumentEntry) { - ArgumentEntry argumentEntry = (ArgumentEntry)thing; - return (T)new ArgumentEntry( - renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), - argumentEntry.getIndex(), - argumentEntry.getName() - ); - } 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 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.getClassEntry(), + newMethodEntry.getName(), + methodEntry.getSignature() + ); + } + return thing; + } else if (thing instanceof ArgumentEntry) { + ArgumentEntry argumentEntry = (ArgumentEntry) thing; + return (T) new ArgumentEntry( + renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } 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 FieldEntry) { - FieldEntry fieldEntry = (FieldEntry) thing; - return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); - } else if (thing instanceof ConstructorEntry) { - ConstructorEntry constructorEntry = (ConstructorEntry) thing; - return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); - } else if (thing instanceof MethodEntry) { - MethodEntry methodEntry = (MethodEntry) thing; - return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); - } else if (thing instanceof ArgumentEntry) { - ArgumentEntry argumentEntry = (ArgumentEntry) thing; - return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName()); - } 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 Signature) { - return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); - } else if (thing instanceof Type) { - return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); - } + @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 FieldEntry) { + FieldEntry fieldEntry = (FieldEntry) thing; + return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); + } else if (thing instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry) thing; + return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); + } else if (thing instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry) thing; + return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); + } else if (thing instanceof ArgumentEntry) { + ArgumentEntry argumentEntry = (ArgumentEntry) thing; + return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName()); + } 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 Signature) { + return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); + } else if (thing instanceof Type) { + return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); + } - return thing; - } + return thing; + } } diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 70cd059..34d2eff 100644 --- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -8,72 +8,73 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ -package cuchaz.enigma.analysis; -import javax.swing.tree.DefaultMutableTreeNode; +package cuchaz.enigma.analysis; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Translator; +import javax.swing.tree.DefaultMutableTreeNode; + public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { - private Translator deobfuscatingTranslator; - private FieldEntry entry; - private EntryReference reference; - private Access access; + private Translator deobfuscatingTranslator; + private FieldEntry entry; + private EntryReference reference; + private Access access; - public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.entry = entry; - this.reference = null; - } + public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.entry = entry; + this.reference = null; + } - private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.entry = reference.entry; - this.reference = reference; - this.access = access; - } + private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.entry = reference.entry; + this.reference = reference; + this.access = access; + } - @Override - public FieldEntry getEntry() { - return this.entry; - } + @Override + public FieldEntry getEntry() { + return this.entry; + } - @Override - public EntryReference getReference() { - return this.reference; - } + @Override + public EntryReference getReference() { + return this.reference; + } - @Override - public String toString() { - if (this.reference != null) { - return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); - } - return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); - } + @Override + public String toString() { + if (this.reference != null) { + return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); + } + return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); + } - public void load(JarIndex index, boolean recurse) { - // get all the child nodes - if (this.reference == null) { - for (EntryReference reference : index.getFieldReferences(this.entry)) { - add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); - } - } else { - for (EntryReference reference : index.getBehaviorReferences(this.reference.context)) { - add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); - } - } + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + if (this.reference == null) { + for (EntryReference reference : index.getFieldReferences(this.entry)) { + add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); + } + } else { + for (EntryReference reference : index.getBehaviorReferences(this.reference.context)) { + add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); + } + } - if (recurse && children != null) { - for (Object node : children) { - if (node instanceof BehaviorReferenceTreeNode) { - ((BehaviorReferenceTreeNode) node).load(index, true); - } else if (node instanceof FieldReferenceTreeNode) { - ((FieldReferenceTreeNode) node).load(index, true); - } - } - } - } + if (recurse && children != null) { + for (Object node : children) { + if (node instanceof BehaviorReferenceTreeNode) { + ((BehaviorReferenceTreeNode) node).load(index, true); + } else if (node instanceof FieldReferenceTreeNode) { + ((FieldReferenceTreeNode) node).load(index, true); + } + } + } + } } diff --git a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java index 0d18105..87d3797 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java @@ -8,9 +8,17 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.Lists; +import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -21,103 +29,95 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import cuchaz.enigma.Constants; -import cuchaz.enigma.mapping.ClassEntry; -import javassist.ByteArrayClassPath; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; -import javassist.bytecode.Descriptor; - public class JarClassIterator implements Iterator { - private JarFile jar; - private Iterator iter; - - public JarClassIterator(JarFile jar) { - this.jar = jar; - - // get the jar entries that correspond to classes - List classEntries = Lists.newArrayList(); - Enumeration entries = this.jar.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - - // is this a class file? - if (entry.getName().endsWith(".class")) { - classEntries.add(entry); - } - } - this.iter = classEntries.iterator(); - } - - @Override - public boolean hasNext() { - return this.iter.hasNext(); - } - - @Override - public CtClass next() { - JarEntry entry = this.iter.next(); - try { - return getClass(this.jar, entry); - } catch (IOException | NotFoundException ex) { - throw new Error("Unable to load class: " + entry.getName()); - } - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - public static List getClassEntries(JarFile jar) { - List classEntries = Lists.newArrayList(); - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - - // is this a class file? - if (!entry.isDirectory() && entry.getName().endsWith(".class")) { - classEntries.add(getClassEntry(entry)); - } - } - return classEntries; - } - - public static Iterable classes(final JarFile jar) { - return () -> new JarClassIterator(jar); - } - - private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { - // read the class into a buffer - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buf = new byte[Constants.KiB]; - int totalNumBytesRead = 0; - InputStream in = jar.getInputStream(entry); - while (in.available() > 0) { - int numBytesRead = in.read(buf); - if (numBytesRead < 0) { - break; - } - bos.write(buf, 0, numBytesRead); - - // sanity checking - totalNumBytesRead += numBytesRead; - if (totalNumBytesRead > Constants.MiB) { - throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); - } - } - - // get a javassist handle for the class - String className = Descriptor.toJavaName(getClassEntry(entry).getName()); - ClassPool classPool = new ClassPool(); - classPool.appendSystemPath(); - classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); - return classPool.get(className); - } - - private static ClassEntry getClassEntry(JarEntry entry) { - return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); - } + private JarFile jar; + private Iterator iter; + + public JarClassIterator(JarFile jar) { + this.jar = jar; + + // get the jar entries that correspond to classes + List classEntries = Lists.newArrayList(); + Enumeration entries = this.jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if (entry.getName().endsWith(".class")) { + classEntries.add(entry); + } + } + this.iter = classEntries.iterator(); + } + + public static List getClassEntries(JarFile jar) { + List classEntries = Lists.newArrayList(); + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if (!entry.isDirectory() && entry.getName().endsWith(".class")) { + classEntries.add(getClassEntry(entry)); + } + } + return classEntries; + } + + public static Iterable classes(final JarFile jar) { + return () -> new JarClassIterator(jar); + } + + private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + InputStream in = jar.getInputStream(entry); + while (in.available() > 0) { + int numBytesRead = in.read(buf); + if (numBytesRead < 0) { + break; + } + bos.write(buf, 0, numBytesRead); + + // sanity checking + totalNumBytesRead += numBytesRead; + if (totalNumBytesRead > Constants.MiB) { + throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); + } + } + + // get a javassist handle for the class + String className = Descriptor.toJavaName(getClassEntry(entry).getName()); + ClassPool classPool = new ClassPool(); + classPool.appendSystemPath(); + classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); + return classPool.get(className); + } + + private static ClassEntry getClassEntry(JarEntry entry) { + return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); + } + + @Override + public boolean hasNext() { + return this.iter.hasNext(); + } + + @Override + public CtClass next() { + JarEntry entry = this.iter.next(); + try { + return getClass(this.jar, entry); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + entry.getName()); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index e8f74cc..ea87dda 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -8,825 +8,824 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.*; - -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.jar.JarFile; - import cuchaz.enigma.mapping.*; import cuchaz.enigma.mapping.Translator; import javassist.*; import javassist.bytecode.*; import javassist.expr.*; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.jar.JarFile; + public class JarIndex { - private Set obfClassEntries; - private TranslationIndex translationIndex; - private Map access; - private Multimap fields; - private Multimap behaviors; - private Multimap methodImplementations; - private Multimap> behaviorReferences; - private Multimap> fieldReferences; - private Multimap innerClassesByOuter; - private Map outerClassesByInner; - private Map anonymousClasses; - private Map bridgedMethods; - private Set syntheticMethods; - - public JarIndex() { - this.obfClassEntries = Sets.newHashSet(); - this.translationIndex = new TranslationIndex(); - this.access = Maps.newHashMap(); - this.fields = HashMultimap.create(); - this.behaviors = HashMultimap.create(); - this.methodImplementations = HashMultimap.create(); - this.behaviorReferences = HashMultimap.create(); - this.fieldReferences = HashMultimap.create(); - this.innerClassesByOuter = HashMultimap.create(); - this.outerClassesByInner = Maps.newHashMap(); - this.anonymousClasses = Maps.newHashMap(); - this.bridgedMethods = Maps.newHashMap(); - this.syntheticMethods = Sets.newHashSet(); - } - - public void indexJar(JarFile jar, boolean buildInnerClasses) { - - // step 1: read the class names - this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); - - // step 2: index field/method/constructor access - for (CtClass c : JarClassIterator.classes(jar)) { - for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); - this.access.put(fieldEntry, Access.get(field)); - this.fields.put(fieldEntry.getClassEntry(), fieldEntry); - } - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - this.access.put(behaviorEntry, Access.get(behavior)); - this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); - } - } - - // step 3: index extends, implements, fields, and methods - for (CtClass c : JarClassIterator.classes(jar)) { - this.translationIndex.indexClass(c); - String className = Descriptor.toJvmName(c.getName()); - for (String interfaceName : c.getClassFile().getInterfaces()) { - className = Descriptor.toJvmName(className); - interfaceName = Descriptor.toJvmName(interfaceName); - if (className.equals(interfaceName)) { - throw new IllegalArgumentException("Class cannot be its own interface! " + className); - } - } - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - indexBehavior(behavior); - } - } - - // step 4: index field, method, constructor references - for (CtClass c : JarClassIterator.classes(jar)) { - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - indexBehaviorReferences(behavior); - } - } - - if (buildInnerClasses) { - - // step 5: index inner classes and anonymous classes - for (CtClass c : JarClassIterator.classes(jar)) { - ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); - ClassEntry outerClassEntry = findOuterClass(c); - if (outerClassEntry != null) { - this.innerClassesByOuter.put(outerClassEntry, innerClassEntry); - boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; - assert (innerWasAdded); - - BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); - if (enclosingBehavior != null) { - this.anonymousClasses.put(innerClassEntry, enclosingBehavior); - - // DEBUG - //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); - }/* else { - // DEBUG + private Set obfClassEntries; + private TranslationIndex translationIndex; + private Map access; + private Multimap fields; + private Multimap behaviors; + private Multimap methodImplementations; + private Multimap> behaviorReferences; + private Multimap> fieldReferences; + private Multimap innerClassesByOuter; + private Map outerClassesByInner; + private Map anonymousClasses; + private Map bridgedMethods; + private Set syntheticMethods; + + public JarIndex() { + this.obfClassEntries = Sets.newHashSet(); + this.translationIndex = new TranslationIndex(); + this.access = Maps.newHashMap(); + this.fields = HashMultimap.create(); + this.behaviors = HashMultimap.create(); + this.methodImplementations = HashMultimap.create(); + this.behaviorReferences = HashMultimap.create(); + this.fieldReferences = HashMultimap.create(); + this.innerClassesByOuter = HashMultimap.create(); + this.outerClassesByInner = Maps.newHashMap(); + this.anonymousClasses = Maps.newHashMap(); + this.bridgedMethods = Maps.newHashMap(); + this.syntheticMethods = Sets.newHashSet(); + } + + public void indexJar(JarFile jar, boolean buildInnerClasses) { + + // step 1: read the class names + this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); + + // step 2: index field/method/constructor access + for (CtClass c : JarClassIterator.classes(jar)) { + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); + this.access.put(fieldEntry, Access.get(field)); + this.fields.put(fieldEntry.getClassEntry(), fieldEntry); + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + this.access.put(behaviorEntry, Access.get(behavior)); + this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); + } + } + + // step 3: index extends, implements, fields, and methods + for (CtClass c : JarClassIterator.classes(jar)) { + this.translationIndex.indexClass(c); + String className = Descriptor.toJvmName(c.getName()); + for (String interfaceName : c.getClassFile().getInterfaces()) { + className = Descriptor.toJvmName(className); + interfaceName = Descriptor.toJvmName(interfaceName); + if (className.equals(interfaceName)) { + throw new IllegalArgumentException("Class cannot be its own interface! " + className); + } + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehavior(behavior); + } + } + + // step 4: index field, method, constructor references + for (CtClass c : JarClassIterator.classes(jar)) { + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehaviorReferences(behavior); + } + } + + if (buildInnerClasses) { + + // step 5: index inner classes and anonymous classes + for (CtClass c : JarClassIterator.classes(jar)) { + ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); + ClassEntry outerClassEntry = findOuterClass(c); + if (outerClassEntry != null) { + this.innerClassesByOuter.put(outerClassEntry, innerClassEntry); + boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; + assert (innerWasAdded); + + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); + if (enclosingBehavior != null) { + this.anonymousClasses.put(innerClassEntry, enclosingBehavior); + + // DEBUG + //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); + }/* else { + // DEBUG //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); }*/ - } - } - - // 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.behaviorReferences); - EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); - EntryRenamer.renameClassesInMap(renames, this.access); - } - } - - private void indexBehavior(CtBehavior behavior) { - // get the behavior entry - final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - if (behaviorEntry instanceof MethodEntry) { - MethodEntry methodEntry = (MethodEntry) behaviorEntry; - - // is synthetic - if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) { - syntheticMethods.add(methodEntry); - } - - // index implementation - this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry); - - // look for bridge and bridged methods - CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior); - if (bridgedMethod != null) { - this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); - } - } - // looks like we don't care about constructors here - } - - private void indexBehaviorReferences(CtBehavior behavior) { - // index method calls - final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - try { - behavior.instrument(new ExprEditor() { - @Override - public void edit(MethodCall call) { - MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); - ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry); - if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { - calledMethodEntry = new MethodEntry( - resolvedClassEntry, - calledMethodEntry.getName(), - calledMethodEntry.getSignature() - ); - } - EntryReference reference = new EntryReference<>( - calledMethodEntry, - call.getMethodName(), - behaviorEntry - ); - behaviorReferences.put(calledMethodEntry, reference); - } - - @Override - public void edit(FieldAccess call) { - FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); - ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry); - if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { - calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); - } - EntryReference reference = new EntryReference<>( - calledFieldEntry, - call.getFieldName(), - behaviorEntry - ); - fieldReferences.put(calledFieldEntry, reference); - } - - @Override - public void edit(ConstructorCall call) { - ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); - EntryReference reference = new EntryReference<>( - calledConstructorEntry, - call.getMethodName(), - behaviorEntry - ); - behaviorReferences.put(calledConstructorEntry, reference); - } - - @Override - public void edit(NewExpr call) { - ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); - EntryReference reference = new EntryReference<>( - calledConstructorEntry, - call.getClassName(), - behaviorEntry - ); - behaviorReferences.put(calledConstructorEntry, reference); - } - }); - } catch (CannotCompileException ex) { - throw new Error(ex); - } - } - - private CtMethod getBridgedMethod(CtMethod method) { - - // bridge methods just call another method, cast it to the return type, and return the result - // let's see if we can detect this scenario - - // skip non-synthetic methods - if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { - return null; - } - - // get all the called methods - final List methodCalls = Lists.newArrayList(); - try { - method.instrument(new ExprEditor() { - @Override - public void edit(MethodCall call) { - methodCalls.add(call); - } - }); - } catch (CannotCompileException ex) { - // this is stupid... we're not even compiling anything - throw new Error(ex); - } - - // is there just one? - if (methodCalls.size() != 1) { - return null; - } - MethodCall call = methodCalls.get(0); - - try { - // we have a bridge method! - return call.getMethod(); - } catch (NotFoundException ex) { - // can't find the type? not a bridge method - return null; - } - } - - private ClassEntry findOuterClass(CtClass c) { - - ClassEntry classEntry = EntryFactory.getClassEntry(c); - - // does this class already have an outer class? - if (classEntry.isInnerClass()) { - return classEntry.getOuterClassEntry(); - } - - // inner classes: - // have constructors that can (illegally) set synthetic fields - // the outer class is the only class that calls constructors - - // use the synthetic fields to find the synthetic constructors - for (CtConstructor constructor : c.getDeclaredConstructors()) { - Set syntheticFieldTypes = Sets.newHashSet(); - if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { - continue; - } - - ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); - - // gather the classes from the illegally-set synthetic fields - Set illegallySetClasses = Sets.newHashSet(); - for (String type : syntheticFieldTypes) { - if (type.startsWith("L")) { - ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); - if (isSaneOuterClass(outerClassEntry, classEntry)) { - illegallySetClasses.add(outerClassEntry); - } - } - } - - // who calls this constructor? - Set callerClasses = Sets.newHashSet(); - for (EntryReference reference : getBehaviorReferences(constructorEntry)) { - - // make sure it's not a call to super - if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { - - // is the entry a superclass of the context? - ClassEntry calledClassEntry = reference.entry.getClassEntry(); - ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry()); - if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { - // it's a super call, skip - continue; - } - } - - if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { - callerClasses.add(reference.context.getClassEntry()); - } - } - - // do we have an answer yet? - if (callerClasses.isEmpty()) { - if (illegallySetClasses.size() == 1) { - return illegallySetClasses.iterator().next(); - } else { - System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); - } - } else { - if (callerClasses.size() == 1) { - return callerClasses.iterator().next(); - } else { - // multiple callers, do the illegally set classes narrow it down? - Set intersection = Sets.newHashSet(callerClasses); - intersection.retainAll(illegallySetClasses); - if (intersection.size() == 1) { - return intersection.iterator().next(); - } else { - System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); - } - } - } - } - - return null; - } - - private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { - - // clearly this would be silly - if (outerClassEntry.equals(innerClassEntry)) { - return false; - } - - // is the outer class in the jar? - return this.obfClassEntries.contains(outerClassEntry); - - } - - @SuppressWarnings("unchecked") - private boolean isIllegalConstructor(Set syntheticFieldTypes, CtConstructor constructor) { - - // illegal constructors only set synthetic member fields, then call super() - String className = constructor.getDeclaringClass().getName(); - - // collect all the field accesses, constructor calls, and method calls - final List illegalFieldWrites = Lists.newArrayList(); - final List constructorCalls = Lists.newArrayList(); - try { - constructor.instrument(new ExprEditor() { - @Override - public void edit(FieldAccess fieldAccess) { - if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { - illegalFieldWrites.add(fieldAccess); - } - } - - @Override - public void edit(ConstructorCall constructorCall) { - constructorCalls.add(constructorCall); - } - }); - } catch (CannotCompileException ex) { - // we're not compiling anything... this is stupid - throw new Error(ex); - } - - // are there any illegal field writes? - if (illegalFieldWrites.isEmpty()) { - return false; - } - - // are all the writes to synthetic fields? - for (FieldAccess fieldWrite : illegalFieldWrites) { - - // all illegal writes have to be to the local class - if (!fieldWrite.getClassName().equals(className)) { - System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); - return false; - } - - // find the field - FieldInfo fieldInfo = null; - for (FieldInfo info : (List) constructor.getDeclaringClass().getClassFile().getFields()) { - if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { - fieldInfo = info; - break; - } - } - if (fieldInfo == null) { - // field is in a superclass or something, can't be a local synthetic member - return false; - } - - // is this field synthetic? - boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; - if (isSynthetic) { - syntheticFieldTypes.add(fieldInfo.getDescriptor()); - } else { - System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); - return false; - } - } - - // we passed all the tests! - return true; - } - - private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { - - // is this class already marked anonymous? - EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); - if (enclosingMethodAttribute != null) { - if (enclosingMethodAttribute.methodIndex() > 0) { - return EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(enclosingMethodAttribute.className()), - enclosingMethodAttribute.methodName(), - enclosingMethodAttribute.methodDescriptor() - ); - } else { - // an attribute but no method? assume not anonymous - return null; - } - } - - // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous - InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (innerClassesAttribute != null) { - return null; - } - - ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); - - // anonymous classes: - // can't be abstract - // have only one constructor - // it's called exactly once by the outer class - // the type the instance is assigned to can't be this type - - // is abstract? - if (Modifier.isAbstract(c.getModifiers())) { - return null; - } - - // is there exactly one constructor? - if (c.getDeclaredConstructors().length != 1) { - return null; - } - CtConstructor constructor = c.getDeclaredConstructors()[0]; - - // is this constructor called exactly once? - ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); - Collection> references = getBehaviorReferences(constructorEntry); - if (references.size() != 1) { - return null; - } - - // does the caller use this type? - BehaviorEntry caller = references.iterator().next().context; - for (FieldEntry fieldEntry : getReferencedFields(caller)) { - if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { - // caller references this type, so it can't be anonymous - return null; - } - } - for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { - if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { - return null; - } - } - - return caller; - } - - 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.behaviors.values(); - } - - public Collection getObfBehaviorEntries(ClassEntry classEntry) { - return this.behaviors.get(classEntry); - } - - public TranslationIndex getTranslationIndex() { - return this.translationIndex; - } - - public Access getAccess(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 - ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); - for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { - MethodEntry ancestorMethodEntry = new MethodEntry( - new ClassEntry(ancestorClassEntry), - obfMethodEntry.getName(), - obfMethodEntry.getSignature() - ); - if (containsObfBehavior(ancestorMethodEntry)) { - baseImplementationClassEntry = ancestorClassEntry; - } - } - - // make a root node at the base - MethodEntry methodEntry = new MethodEntry( - baseImplementationClassEntry, - obfMethodEntry.getName(), - obfMethodEntry.getSignature() - ); - MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( - deobfuscatingTranslator, - methodEntry, - containsObfBehavior(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 = new MethodEntry( - interfaceEntry, - obfMethodEntry.getName(), - obfMethodEntry.getSignature() - ); - if (containsObfBehavior(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) { - Set methodEntries = Sets.newHashSet(); - getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); - return methodEntries; - } - - private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { - MethodEntry methodEntry = node.getMethodEntry(); - - if (containsObfBehavior(methodEntry)) { - // collect the entry - methodEntries.add(methodEntry); - } - - // look at bridged methods! - MethodEntry bridgedEntry = getBridgedMethod(methodEntry); - while (bridgedEntry != null) { - methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); - bridgedEntry = getBridgedMethod(bridgedEntry); - } - - // look at interface methods too - for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), 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 (containsObfBehavior(methodEntry)) { - // collect the entry - methodEntries.add(methodEntry); - } - - // look at bridged methods! - MethodEntry bridgedEntry = getBridgedMethod(methodEntry); - while (bridgedEntry != null) { - methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); - bridgedEntry = getBridgedMethod(bridgedEntry); - } - - // 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(BehaviorEntry behaviorEntry) { - // linear search is fast enough for now - Set fieldEntries = Sets.newHashSet(); - for (EntryReference reference : this.fieldReferences.values()) { - if (reference.context == behaviorEntry) { - fieldEntries.add(reference.entry); - } - } - return fieldEntries; - } - - public Collection> getBehaviorReferences(BehaviorEntry behaviorEntry) { - return this.behaviorReferences.get(behaviorEntry); - } - - public Collection getReferencedBehaviors(BehaviorEntry behaviorEntry) { - // linear search is fast enough for now - Set behaviorEntries = Sets.newHashSet(); - for (EntryReference reference : this.behaviorReferences.values()) { - if (reference.context == behaviorEntry) { - behaviorEntries.add(reference.entry); - } - } - return behaviorEntries; - } - - public Collection getInnerClasses(ClassEntry obfOuterClassEntry) { - return this.innerClassesByOuter.get(obfOuterClassEntry); - } - - public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { - return this.outerClassesByInner.get(obfInnerClassEntry); - } - - public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { - return this.anonymousClasses.containsKey(obfInnerClassEntry); - } - - public boolean isSyntheticMethod(MethodEntry methodEntry) { - return this.syntheticMethods.contains(methodEntry); - } - - public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { - return this.anonymousClasses.get(obfInnerClassName); - } - - public Set getInterfaces(String className) { - ClassEntry classEntry = new ClassEntry(className); - Set interfaces = new HashSet<>(); - interfaces.addAll(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(new ClassEntry(className)); - } - - public boolean containsObfClass(ClassEntry obfClassEntry) { - return this.obfClassEntries.contains(obfClassEntry); - } - - public boolean containsObfField(FieldEntry obfFieldEntry) { - return this.access.containsKey(obfFieldEntry); - } - - public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { - return this.access.containsKey(obfBehaviorEntry); - } - - 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 containsObfArgument(ArgumentEntry obfArgumentEntry) { - // check the behavior - if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { - return false; - } - - // check the argument - return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size(); - - } - - 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 BehaviorEntry) { - return containsObfBehavior((BehaviorEntry) obfEntry); - } else if (obfEntry instanceof ArgumentEntry) { - return containsObfArgument((ArgumentEntry) obfEntry); - } else if (obfEntry instanceof LocalVariableEntry) { - // TODO: Implement it - return false; - } else { - throw new Error("Entry type 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; - } + } + } + + // 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.behaviorReferences); + EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); + EntryRenamer.renameClassesInMap(renames, this.access); + } + } + + private void indexBehavior(CtBehavior behavior) { + // get the behavior entry + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + if (behaviorEntry instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry) behaviorEntry; + + // is synthetic + if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) { + syntheticMethods.add(methodEntry); + } + + // index implementation + this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry); + + // look for bridge and bridged methods + CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior); + if (bridgedMethod != null) { + this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); + } + } + // looks like we don't care about constructors here + } + + private void indexBehaviorReferences(CtBehavior behavior) { + // index method calls + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + try { + behavior.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); + ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { + calledMethodEntry = new MethodEntry( + resolvedClassEntry, + calledMethodEntry.getName(), + calledMethodEntry.getSignature() + ); + } + EntryReference reference = new EntryReference<>( + calledMethodEntry, + call.getMethodName(), + behaviorEntry + ); + behaviorReferences.put(calledMethodEntry, reference); + } + + @Override + public void edit(FieldAccess call) { + FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); + ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { + calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); + } + EntryReference reference = new EntryReference<>( + calledFieldEntry, + call.getFieldName(), + behaviorEntry + ); + fieldReferences.put(calledFieldEntry, reference); + } + + @Override + public void edit(ConstructorCall call) { + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); + EntryReference reference = new EntryReference<>( + calledConstructorEntry, + call.getMethodName(), + behaviorEntry + ); + behaviorReferences.put(calledConstructorEntry, reference); + } + + @Override + public void edit(NewExpr call) { + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); + EntryReference reference = new EntryReference<>( + calledConstructorEntry, + call.getClassName(), + behaviorEntry + ); + behaviorReferences.put(calledConstructorEntry, reference); + } + }); + } catch (CannotCompileException ex) { + throw new Error(ex); + } + } + + private CtMethod getBridgedMethod(CtMethod method) { + + // bridge methods just call another method, cast it to the return type, and return the result + // let's see if we can detect this scenario + + // skip non-synthetic methods + if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { + return null; + } + + // get all the called methods + final List methodCalls = Lists.newArrayList(); + try { + method.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + methodCalls.add(call); + } + }); + } catch (CannotCompileException ex) { + // this is stupid... we're not even compiling anything + throw new Error(ex); + } + + // is there just one? + if (methodCalls.size() != 1) { + return null; + } + MethodCall call = methodCalls.get(0); + + try { + // we have a bridge method! + return call.getMethod(); + } catch (NotFoundException ex) { + // can't find the type? not a bridge method + return null; + } + } + + private ClassEntry findOuterClass(CtClass c) { + + ClassEntry classEntry = EntryFactory.getClassEntry(c); + + // does this class already have an outer class? + if (classEntry.isInnerClass()) { + return classEntry.getOuterClassEntry(); + } + + // inner classes: + // have constructors that can (illegally) set synthetic fields + // the outer class is the only class that calls constructors + + // use the synthetic fields to find the synthetic constructors + for (CtConstructor constructor : c.getDeclaredConstructors()) { + Set syntheticFieldTypes = Sets.newHashSet(); + if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { + continue; + } + + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); + + // gather the classes from the illegally-set synthetic fields + Set illegallySetClasses = Sets.newHashSet(); + for (String type : syntheticFieldTypes) { + if (type.startsWith("L")) { + ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); + if (isSaneOuterClass(outerClassEntry, classEntry)) { + illegallySetClasses.add(outerClassEntry); + } + } + } + + // who calls this constructor? + Set callerClasses = Sets.newHashSet(); + for (EntryReference reference : getBehaviorReferences(constructorEntry)) { + + // make sure it's not a call to super + if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { + + // is the entry a superclass of the context? + ClassEntry calledClassEntry = reference.entry.getClassEntry(); + ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry()); + if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { + // it's a super call, skip + continue; + } + } + + if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { + callerClasses.add(reference.context.getClassEntry()); + } + } + + // do we have an answer yet? + if (callerClasses.isEmpty()) { + if (illegallySetClasses.size() == 1) { + return illegallySetClasses.iterator().next(); + } else { + System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); + } + } else { + if (callerClasses.size() == 1) { + return callerClasses.iterator().next(); + } else { + // multiple callers, do the illegally set classes narrow it down? + Set intersection = Sets.newHashSet(callerClasses); + intersection.retainAll(illegallySetClasses); + if (intersection.size() == 1) { + return intersection.iterator().next(); + } else { + System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); + } + } + } + } + + return null; + } + + private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { + + // clearly this would be silly + if (outerClassEntry.equals(innerClassEntry)) { + return false; + } + + // is the outer class in the jar? + return this.obfClassEntries.contains(outerClassEntry); + + } + + @SuppressWarnings("unchecked") + private boolean isIllegalConstructor(Set syntheticFieldTypes, CtConstructor constructor) { + + // illegal constructors only set synthetic member fields, then call super() + String className = constructor.getDeclaringClass().getName(); + + // collect all the field accesses, constructor calls, and method calls + final List illegalFieldWrites = Lists.newArrayList(); + final List constructorCalls = Lists.newArrayList(); + try { + constructor.instrument(new ExprEditor() { + @Override + public void edit(FieldAccess fieldAccess) { + if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { + illegalFieldWrites.add(fieldAccess); + } + } + + @Override + public void edit(ConstructorCall constructorCall) { + constructorCalls.add(constructorCall); + } + }); + } catch (CannotCompileException ex) { + // we're not compiling anything... this is stupid + throw new Error(ex); + } + + // are there any illegal field writes? + if (illegalFieldWrites.isEmpty()) { + return false; + } + + // are all the writes to synthetic fields? + for (FieldAccess fieldWrite : illegalFieldWrites) { + + // all illegal writes have to be to the local class + if (!fieldWrite.getClassName().equals(className)) { + System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); + return false; + } + + // find the field + FieldInfo fieldInfo = null; + for (FieldInfo info : (List) constructor.getDeclaringClass().getClassFile().getFields()) { + if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { + fieldInfo = info; + break; + } + } + if (fieldInfo == null) { + // field is in a superclass or something, can't be a local synthetic member + return false; + } + + // is this field synthetic? + boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; + if (isSynthetic) { + syntheticFieldTypes.add(fieldInfo.getDescriptor()); + } else { + System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); + return false; + } + } + + // we passed all the tests! + return true; + } + + private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { + + // is this class already marked anonymous? + EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); + if (enclosingMethodAttribute != null) { + if (enclosingMethodAttribute.methodIndex() > 0) { + return EntryFactory.getBehaviorEntry( + Descriptor.toJvmName(enclosingMethodAttribute.className()), + enclosingMethodAttribute.methodName(), + enclosingMethodAttribute.methodDescriptor() + ); + } else { + // an attribute but no method? assume not anonymous + return null; + } + } + + // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous + InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (innerClassesAttribute != null) { + return null; + } + + ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + + // anonymous classes: + // can't be abstract + // have only one constructor + // it's called exactly once by the outer class + // the type the instance is assigned to can't be this type + + // is abstract? + if (Modifier.isAbstract(c.getModifiers())) { + return null; + } + + // is there exactly one constructor? + if (c.getDeclaredConstructors().length != 1) { + return null; + } + CtConstructor constructor = c.getDeclaredConstructors()[0]; + + // is this constructor called exactly once? + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); + Collection> references = getBehaviorReferences(constructorEntry); + if (references.size() != 1) { + return null; + } + + // does the caller use this type? + BehaviorEntry caller = references.iterator().next().context; + for (FieldEntry fieldEntry : getReferencedFields(caller)) { + if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { + // caller references this type, so it can't be anonymous + return null; + } + } + for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { + if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { + return null; + } + } + + return caller; + } + + 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.behaviors.values(); + } + + public Collection getObfBehaviorEntries(ClassEntry classEntry) { + return this.behaviors.get(classEntry); + } + + public TranslationIndex getTranslationIndex() { + return this.translationIndex; + } + + public Access getAccess(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 + ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); + for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { + MethodEntry ancestorMethodEntry = new MethodEntry( + new ClassEntry(ancestorClassEntry), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if (containsObfBehavior(ancestorMethodEntry)) { + baseImplementationClassEntry = ancestorClassEntry; + } + } + + // make a root node at the base + MethodEntry methodEntry = new MethodEntry( + baseImplementationClassEntry, + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( + deobfuscatingTranslator, + methodEntry, + containsObfBehavior(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 = new MethodEntry( + interfaceEntry, + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if (containsObfBehavior(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) { + Set methodEntries = Sets.newHashSet(); + getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); + return methodEntries; + } + + private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { + MethodEntry methodEntry = node.getMethodEntry(); + + if (containsObfBehavior(methodEntry)) { + // collect the entry + methodEntries.add(methodEntry); + } + + // look at bridged methods! + MethodEntry bridgedEntry = getBridgedMethod(methodEntry); + while (bridgedEntry != null) { + methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); + bridgedEntry = getBridgedMethod(bridgedEntry); + } + + // look at interface methods too + for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), 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 (containsObfBehavior(methodEntry)) { + // collect the entry + methodEntries.add(methodEntry); + } + + // look at bridged methods! + MethodEntry bridgedEntry = getBridgedMethod(methodEntry); + while (bridgedEntry != null) { + methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); + bridgedEntry = getBridgedMethod(bridgedEntry); + } + + // 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(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set fieldEntries = Sets.newHashSet(); + for (EntryReference reference : this.fieldReferences.values()) { + if (reference.context == behaviorEntry) { + fieldEntries.add(reference.entry); + } + } + return fieldEntries; + } + + public Collection> getBehaviorReferences(BehaviorEntry behaviorEntry) { + return this.behaviorReferences.get(behaviorEntry); + } + + public Collection getReferencedBehaviors(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set behaviorEntries = Sets.newHashSet(); + for (EntryReference reference : this.behaviorReferences.values()) { + if (reference.context == behaviorEntry) { + behaviorEntries.add(reference.entry); + } + } + return behaviorEntries; + } + + public Collection getInnerClasses(ClassEntry obfOuterClassEntry) { + return this.innerClassesByOuter.get(obfOuterClassEntry); + } + + public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { + return this.outerClassesByInner.get(obfInnerClassEntry); + } + + public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { + return this.anonymousClasses.containsKey(obfInnerClassEntry); + } + + public boolean isSyntheticMethod(MethodEntry methodEntry) { + return this.syntheticMethods.contains(methodEntry); + } + + public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { + return this.anonymousClasses.get(obfInnerClassName); + } + + public Set getInterfaces(String className) { + ClassEntry classEntry = new ClassEntry(className); + Set interfaces = new HashSet<>(); + interfaces.addAll(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(new ClassEntry(className)); + } + + public boolean containsObfClass(ClassEntry obfClassEntry) { + return this.obfClassEntries.contains(obfClassEntry); + } + + public boolean containsObfField(FieldEntry obfFieldEntry) { + return this.access.containsKey(obfFieldEntry); + } + + public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { + return this.access.containsKey(obfBehaviorEntry); + } + + 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 containsObfArgument(ArgumentEntry obfArgumentEntry) { + // check the behavior + if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { + return false; + } + + // check the argument + return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size(); + + } + + 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 BehaviorEntry) { + return containsObfBehavior((BehaviorEntry) obfEntry); + } else if (obfEntry instanceof ArgumentEntry) { + return containsObfArgument((ArgumentEntry) obfEntry); + } else if (obfEntry instanceof LocalVariableEntry) { + // TODO: Implement it + return false; + } else { + throw new Error("Entry type 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 9bd6219..bacb1aa 100644 --- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -8,87 +8,86 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.Lists; - -import java.util.List; - -import javax.swing.tree.DefaultMutableTreeNode; - import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; +import javax.swing.tree.DefaultMutableTreeNode; +import java.util.List; + public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { - private Translator deobfuscatingTranslator; - private MethodEntry entry; - - public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { - if (entry == null) { - throw new IllegalArgumentException("Entry cannot be null!"); - } - - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.entry = entry; - } - - public MethodEntry getMethodEntry() { - return this.entry; - } - - public String getDeobfClassName() { - return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); - } - - public String getDeobfMethodName() { - return this.deobfuscatingTranslator.translate(this.entry); - } - - @Override - public String toString() { - String className = getDeobfClassName(); - if (className == null) { - className = this.entry.getClassName(); - } - - String methodName = getDeobfMethodName(); - if (methodName == null) { - methodName = this.entry.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.getSignature() - ); - if (index.containsObfBehavior(methodEntry)) { - nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); - } - } - - // add them to this node - nodes.forEach(this::add); - } - - public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { - // is this the node? - if (node.getMethodEntry().equals(entry)) { - return node; - } - - // recurse - for (int i = 0; i < node.getChildCount(); i++) { - MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); - if (foundNode != null) { - return foundNode; - } - } - return null; - } + private Translator deobfuscatingTranslator; + private MethodEntry entry; + + public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { + if (entry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); + } + + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.entry = entry; + } + + public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { + // is this the node? + if (node.getMethodEntry().equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } + + public MethodEntry getMethodEntry() { + return this.entry; + } + + public String getDeobfClassName() { + return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); + } + + public String getDeobfMethodName() { + return this.deobfuscatingTranslator.translate(this.entry); + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = this.entry.getClassName(); + } + + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = this.entry.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.getSignature() + ); + if (index.containsObfBehavior(methodEntry)) { + nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); + } + } + + // add them to this node + nodes.forEach(this::add); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index b65b8c1..4f84dd0 100644 --- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -8,97 +8,96 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.Lists; - -import java.util.List; - -import javax.swing.tree.DefaultMutableTreeNode; - import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; +import javax.swing.tree.DefaultMutableTreeNode; +import java.util.List; + public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { - private Translator deobfuscatingTranslator; - private MethodEntry entry; - private boolean isImplemented; - - public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { - this.deobfuscatingTranslator = deobfuscatingTranslator; - this.entry = entry; - this.isImplemented = isImplemented; - } - - public MethodEntry getMethodEntry() { - return this.entry; - } - - public String getDeobfClassName() { - return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); - } - - public String getDeobfMethodName() { - return this.deobfuscatingTranslator.translate(this.entry); - } - - public boolean isImplemented() { - return this.isImplemented; - } - - @Override - public String toString() { - String className = getDeobfClassName(); - if (className == null) { - className = this.entry.getClassName(); - } - - if (!this.isImplemented) { - return className; - } else { - String methodName = getDeobfMethodName(); - if (methodName == null) { - methodName = this.entry.getName(); - } - return className + "." + methodName + "()"; - } - } - - public void load(JarIndex index, boolean recurse) { - // get all the child nodes - List nodes = Lists.newArrayList(); - for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { - MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() - ); - nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry) - )); - } - - // add them to this node - nodes.forEach(this::add); - - if (recurse) { - for (MethodInheritanceTreeNode node : nodes) { - node.load(index, true); - } - } - } - - public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { - // is this the node? - if (node.getMethodEntry().equals(entry)) { - return node; - } - - // recurse - for (int i = 0; i < node.getChildCount(); i++) { - MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); - if (foundNode != null) { - return foundNode; - } - } - return null; - } + private Translator deobfuscatingTranslator; + private MethodEntry entry; + private boolean isImplemented; + + public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { + this.deobfuscatingTranslator = deobfuscatingTranslator; + this.entry = entry; + this.isImplemented = isImplemented; + } + + public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { + // is this the node? + if (node.getMethodEntry().equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } + + public MethodEntry getMethodEntry() { + return this.entry; + } + + public String getDeobfClassName() { + return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); + } + + public String getDeobfMethodName() { + return this.deobfuscatingTranslator.translate(this.entry); + } + + public boolean isImplemented() { + return this.isImplemented; + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = this.entry.getClassName(); + } + + if (!this.isImplemented) { + return className; + } else { + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = this.entry.getName(); + } + return className + "." + methodName + "()"; + } + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + List nodes = Lists.newArrayList(); + for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { + MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() + ); + nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry) + )); + } + + // add them to this node + nodes.forEach(this::add); + + if (recurse) { + for (MethodInheritanceTreeNode node : nodes) { + node.load(index, true); + } + } + } } diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java index 9392346..0469363 100644 --- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -8,12 +8,13 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import cuchaz.enigma.mapping.Entry; public interface ReferenceTreeNode { - E getEntry(); + E getEntry(); - EntryReference getReference(); + EntryReference getReference(); } diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java index 719930e..19250c8 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java @@ -8,174 +8,173 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + 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.strobel.decompiler.languages.Region; import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.Identifier; +import cuchaz.enigma.mapping.Entry; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.TreeMap; -import cuchaz.enigma.mapping.Entry; - public class SourceIndex { - private String source; - private TreeMap> tokenToReference; - private Multimap, Token> referenceToTokens; - private Map declarationToToken; - private List lineOffsets; - private boolean ignoreBadTokens; - - public SourceIndex(String source) { - this(source, true); - } - - public SourceIndex(String source, boolean ignoreBadTokens) { - this.source = source; - this.ignoreBadTokens = ignoreBadTokens; - this.tokenToReference = Maps.newTreeMap(); - this.referenceToTokens = HashMultimap.create(); - this.declarationToToken = Maps.newHashMap(); - this.lineOffsets = Lists.newArrayList(); - - // count the lines - this.lineOffsets.add(0); - for (int i = 0; i < source.length(); i++) { - if (source.charAt(i) == '\n') { - this.lineOffsets.add(i + 1); - } - } - } - - public String getSource() { - return this.source; - } - - public Token getToken(AstNode node) { - - // get the text of the node - String name = ""; - if (node instanceof Identifier) { - name = ((Identifier) node).getName(); - } - - // get a token for this node's region - Region region = node.getRegion(); - if (region.getBeginLine() == 0 || region.getEndLine() == 0) { - // DEBUG - System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); - return null; - } - Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); - if (token.start == 0) { - // DEBUG - System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); - return null; - } - - // DEBUG - // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); - - // if the token has a $ in it, something's wrong. Ignore this token - if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { - // DEBUG - System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); - return null; - } - - return token; - } - - public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { - Token token = getToken(node); - if (token != null) { - EntryReference deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); - this.tokenToReference.put(token, deobfReference); - this.referenceToTokens.put(deobfReference, token); - } - } - - public void addDeclaration(AstNode node, Entry deobfEntry) { - Token token = getToken(node); - if (token != null) { - EntryReference reference = new EntryReference<>(deobfEntry, token.text); - this.tokenToReference.put(token, reference); - this.referenceToTokens.put(reference, token); - this.declarationToToken.put(deobfEntry, token); - } - } - - public Token getReferenceToken(int pos) { - Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); - if (token != null && token.contains(pos)) { - return token; - } - return null; - } - - public Collection getReferenceTokens(EntryReference deobfReference) { - return this.referenceToTokens.get(deobfReference); - } - - public EntryReference 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); - this.referenceToTokens.putAll(newDeobfReference, tokens); - } - - public Iterable referenceTokens() { - return this.tokenToReference.keySet(); - } - - public Iterable declarationTokens() { - return this.declarationToToken.values(); - } - - public Iterable declarations() { - return this.declarationToToken.keySet(); - } - - public Token getDeclarationToken(Entry deobfEntry) { - return this.declarationToToken.get(deobfEntry); - } - - public int getLineNumber(int pos) { - // line number is 1-based - int line = 0; - for (Integer offset : this.lineOffsets) { - if (offset > pos) { - break; - } - line++; - } - return line; - } - - public int getColumnNumber(int pos) { - // column number is 1-based - return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; - } - - private int toPos(int line, int col) { - // line and col are 1-based - return this.lineOffsets.get(line - 1) + col - 1; - } + private String source; + private TreeMap> tokenToReference; + private Multimap, Token> referenceToTokens; + private Map declarationToToken; + private List lineOffsets; + private boolean ignoreBadTokens; + + public SourceIndex(String source) { + this(source, true); + } + + public SourceIndex(String source, boolean ignoreBadTokens) { + this.source = source; + this.ignoreBadTokens = ignoreBadTokens; + this.tokenToReference = Maps.newTreeMap(); + this.referenceToTokens = HashMultimap.create(); + this.declarationToToken = Maps.newHashMap(); + this.lineOffsets = Lists.newArrayList(); + + // count the lines + this.lineOffsets.add(0); + for (int i = 0; i < source.length(); i++) { + if (source.charAt(i) == '\n') { + this.lineOffsets.add(i + 1); + } + } + } + + public String getSource() { + return this.source; + } + + public Token getToken(AstNode node) { + + // get the text of the node + String name = ""; + if (node instanceof Identifier) { + name = ((Identifier) node).getName(); + } + + // get a token for this node's region + Region region = node.getRegion(); + if (region.getBeginLine() == 0 || region.getEndLine() == 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); + return null; + } + Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); + if (token.start == 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); + return null; + } + + // DEBUG + // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); + + // if the token has a $ in it, something's wrong. Ignore this token + if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); + return null; + } + + return token; + } + + public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { + Token token = getToken(node); + if (token != null) { + EntryReference deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); + this.tokenToReference.put(token, deobfReference); + this.referenceToTokens.put(deobfReference, token); + } + } + + public void addDeclaration(AstNode node, Entry deobfEntry) { + Token token = getToken(node); + if (token != null) { + EntryReference reference = new EntryReference<>(deobfEntry, token.text); + this.tokenToReference.put(token, reference); + this.referenceToTokens.put(reference, token); + this.declarationToToken.put(deobfEntry, token); + } + } + + public Token getReferenceToken(int pos) { + Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); + if (token != null && token.contains(pos)) { + return token; + } + return null; + } + + public Collection getReferenceTokens(EntryReference deobfReference) { + return this.referenceToTokens.get(deobfReference); + } + + public EntryReference 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); + this.referenceToTokens.putAll(newDeobfReference, tokens); + } + + public Iterable referenceTokens() { + return this.tokenToReference.keySet(); + } + + public Iterable declarationTokens() { + return this.declarationToToken.values(); + } + + public Iterable declarations() { + return this.declarationToToken.keySet(); + } + + public Token getDeclarationToken(Entry deobfEntry) { + return this.declarationToToken.get(deobfEntry); + } + + public int getLineNumber(int pos) { + // line number is 1-based + int line = 0; + for (Integer offset : this.lineOffsets) { + if (offset > pos) { + break; + } + line++; + } + return line; + } + + public int getColumnNumber(int pos) { + // column number is 1-based + return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; + } + + private int toPos(int line, int col) { + // line and col are 1-based + return this.lineOffsets.get(line - 1) + col - 1; + } } diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index bfd5a56..4febf25 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.google.common.collect.HashMultimap; @@ -26,186 +27,179 @@ import java.util.Map; public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { - private BehaviorEntry behaviorEntry; - - // TODO: Really fix Procyon index problem with inner classes - private int argumentPosition; - private int localsPosition; - private Multimap unmatchedIdentifier = HashMultimap.create(); - private Map identifierEntryCache = new HashMap<>(); - - public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { - this.behaviorEntry = behaviorEntry; - this.argumentPosition = 0; - this.localsPosition = 0; - } - - @Override - public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { - MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); - - // get the behavior entry - ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - BehaviorEntry behaviorEntry = null; - if (ref instanceof MethodReference) { - MethodReference methodRef = (MethodReference) ref; - if (methodRef.isConstructor()) { - behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); - } else if (methodRef.isTypeInitializer()) { - behaviorEntry = new ConstructorEntry(classEntry); - } else { - behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); - } - } - if (behaviorEntry != null) { - // get the node for the token - AstNode tokenNode = null; - if (node.getTarget() instanceof MemberReferenceExpression) { - tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); - } else if (node.getTarget() instanceof SuperReferenceExpression) { - tokenNode = node.getTarget(); - } else if (node.getTarget() instanceof ThisReferenceExpression) { - tokenNode = node.getTarget(); - } - if (tokenNode != null) { - index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); - } - } - - // Check for identifier - node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) - .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); - return recurse(node, index); - } - - @Override - public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { - MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); - if (ref != null) { - // make sure this is actually a field - if (ref.getErasedSignature().indexOf('(') >= 0) { - throw new Error("Expected a field here! got " + ref); - } - - ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); - index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); - } - - return recurse(node, index); - } - - @Override - public Void visitSimpleType(SimpleType node, SourceIndex index) { - TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); - if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { - ClassEntry classEntry = new ClassEntry(ref.getInternalName()); - index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); - } - - return recurse(node, index); - } - - @Override - public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { - ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); - if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) - { - ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), - argumentPosition++, node.getName()); - Identifier identifier = node.getNameToken(); - // cache the argument entry and the identifier - identifierEntryCache.put(identifier.getName(), argumentEntry); - index.addDeclaration(identifier, argumentEntry); - } - - return recurse(node, index); - } - - @Override - public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { - MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); - if (ref != null) { - ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); - index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); - } - else - this.checkIdentifier(node, index); - return recurse(node, index); - } - - private void checkIdentifier(IdentifierExpression node, SourceIndex index) - { - if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! - index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier())); - else - unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! - } - - private void addDeclarationToUnmatched(String key, SourceIndex index) - { - Entry entry = identifierEntryCache.get(key); - - // This cannot happened in theory - if (entry == null) - return; - for (Identifier identifier : unmatchedIdentifier.get(key)) - index.addDeclaration(identifier, entry); - unmatchedIdentifier.removeAll(key); - } - - @Override - public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { - MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); - if (ref != null) { - ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); - if (node.getType() instanceof SimpleType) { - SimpleType simpleTypeNode = (SimpleType) node.getType(); - index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); - } - } - - return recurse(node, index); - } - - @Override - public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { - if (node.getVariableType() instanceof SimpleType) - { - SimpleType type = (SimpleType) node.getVariableType(); - TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); - Identifier identifier = node.getVariableNameToken(); - String signature = Descriptor.of(typeReference.getErasedDescription()); - LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature)); - identifierEntryCache.put(identifier.getName(), localVariableEntry); - addDeclarationToUnmatched(identifier.getName(), index); - index.addDeclaration(identifier, localVariableEntry); - } - return recurse(node, index); - } - - @Override - public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { - AstNodeCollection variables = node.getVariables(); - - // Single assignation - if (variables.size() == 1) - { - VariableInitializer initializer = variables.firstOrNullObject(); - if (initializer != null && node.getType() instanceof SimpleType) - { - SimpleType type = (SimpleType) node.getType(); - TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); - String signature = Descriptor.of(typeReference.getErasedDescription()); - Identifier identifier = initializer.getNameToken(); - LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature)); - identifierEntryCache.put(identifier.getName(), localVariableEntry); - addDeclarationToUnmatched(identifier.getName(), index); - index.addDeclaration(identifier, localVariableEntry); - } - } - return recurse(node, index); - } + private BehaviorEntry behaviorEntry; + + // TODO: Really fix Procyon index problem with inner classes + private int argumentPosition; + private int localsPosition; + private Multimap unmatchedIdentifier = HashMultimap.create(); + private Map identifierEntryCache = new HashMap<>(); + + public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { + this.behaviorEntry = behaviorEntry; + this.argumentPosition = 0; + this.localsPosition = 0; + } + + @Override + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + + // get the behavior entry + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + BehaviorEntry behaviorEntry = null; + if (ref instanceof MethodReference) { + MethodReference methodRef = (MethodReference) ref; + if (methodRef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); + } else if (methodRef.isTypeInitializer()) { + behaviorEntry = new ConstructorEntry(classEntry); + } else { + behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); + } + } + if (behaviorEntry != null) { + // get the node for the token + AstNode tokenNode = null; + if (node.getTarget() instanceof MemberReferenceExpression) { + tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); + } else if (node.getTarget() instanceof SuperReferenceExpression) { + tokenNode = node.getTarget(); + } else if (node.getTarget() instanceof ThisReferenceExpression) { + tokenNode = node.getTarget(); + } + if (tokenNode != null) { + index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); + } + } + + // Check for identifier + node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) + .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); + return recurse(node, index); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + // make sure this is actually a field + if (ref.getErasedSignature().indexOf('(') >= 0) { + throw new Error("Expected a field here! got " + ref); + } + + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); + index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); + if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) { + ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), + argumentPosition++, node.getName()); + Identifier identifier = node.getNameToken(); + // cache the argument entry and the identifier + identifierEntryCache.put(identifier.getName(), argumentEntry); + index.addDeclaration(identifier, argumentEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); + index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); + } else + this.checkIdentifier(node, index); + return recurse(node, index); + } + + private void checkIdentifier(IdentifierExpression node, SourceIndex index) { + if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! + index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier())); + else + unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! + } + + private void addDeclarationToUnmatched(String key, SourceIndex index) { + Entry entry = identifierEntryCache.get(key); + + // This cannot happened in theory + if (entry == null) + return; + for (Identifier identifier : unmatchedIdentifier.get(key)) + index.addDeclaration(identifier, entry); + unmatchedIdentifier.removeAll(key); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); + if (node.getType() instanceof SimpleType) { + SimpleType simpleTypeNode = (SimpleType) node.getType(); + index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); + } + } + + return recurse(node, index); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { + if (node.getVariableType() instanceof SimpleType) { + SimpleType type = (SimpleType) node.getVariableType(); + TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); + Identifier identifier = node.getVariableNameToken(); + String signature = Descriptor.of(typeReference.getErasedDescription()); + LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature)); + identifierEntryCache.put(identifier.getName(), localVariableEntry); + addDeclarationToUnmatched(identifier.getName(), index); + index.addDeclaration(identifier, localVariableEntry); + } + return recurse(node, index); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { + AstNodeCollection variables = node.getVariables(); + + // Single assignation + if (variables.size() == 1) { + VariableInitializer initializer = variables.firstOrNullObject(); + if (initializer != null && node.getType() instanceof SimpleType) { + SimpleType type = (SimpleType) node.getType(); + TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); + String signature = Descriptor.of(typeReference.getErasedDescription()); + Identifier identifier = initializer.getNameToken(); + LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature)); + identifierEntryCache.put(identifier.getName(), localVariableEntry); + addDeclarationToUnmatched(identifier.getName(), index); + index.addDeclaration(identifier, localVariableEntry); + } + } + return recurse(node, index); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 2a21222..1148216 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.strobel.assembler.metadata.FieldDefinition; @@ -20,79 +21,79 @@ import cuchaz.enigma.mapping.*; public class SourceIndexClassVisitor extends SourceIndexVisitor { - private ClassEntry classEntry; + private ClassEntry classEntry; - public SourceIndexClassVisitor(ClassEntry classEntry) { - this.classEntry = classEntry; - } + public SourceIndexClassVisitor(ClassEntry classEntry) { + this.classEntry = classEntry; + } - @Override - public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { - // is this this class, or a subtype? - TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getInternalName()); - if (!classEntry.equals(this.classEntry)) { - // it's a sub-type, recurse - index.addDeclaration(node.getNameToken(), classEntry); - return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); - } + @Override + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { + // is this this class, or a subtype? + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + if (!classEntry.equals(this.classEntry)) { + // it's a sub-type, recurse + index.addDeclaration(node.getNameToken(), classEntry); + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); + } - return recurse(node, index); - } + return recurse(node, index); + } - @Override - public Void visitSimpleType(SimpleType node, SourceIndex index) { - TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); - if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { - ClassEntry classEntry = new ClassEntry(ref.getInternalName()); - index.addReference(node.getIdentifierToken(), classEntry, this.classEntry); - } + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, this.classEntry); + } - return recurse(node, index); - } + return recurse(node, index); + } - @Override - public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { - MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); - AstNode tokenNode = node.getNameToken(); - if (behaviorEntry instanceof ConstructorEntry) { - ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; - if (constructorEntry.isStatic()) { - // for static initializers, check elsewhere for the token node - tokenNode = node.getModifiers().firstOrNullObject(); - } - } - index.addDeclaration(tokenNode, behaviorEntry); - return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); - } + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); + AstNode tokenNode = node.getNameToken(); + if (behaviorEntry instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; + if (constructorEntry.isStatic()) { + // for static initializers, check elsewhere for the token node + tokenNode = node.getModifiers().firstOrNullObject(); + } + } + index.addDeclaration(tokenNode, behaviorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); + } - @Override - public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { - MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); - index.addDeclaration(node.getNameToken(), constructorEntry); - return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); - } + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); + index.addDeclaration(node.getNameToken(), constructorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); + } - @Override - public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { - FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); - assert (node.getVariables().size() == 1); - VariableInitializer variable = node.getVariables().firstOrNullObject(); - index.addDeclaration(variable.getNameToken(), fieldEntry); + @Override + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); + assert (node.getVariables().size() == 1); + VariableInitializer variable = node.getVariables().firstOrNullObject(); + index.addDeclaration(variable.getNameToken(), fieldEntry); - return recurse(node, index); - } + return recurse(node, index); + } - @Override - public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { - // treat enum declarations as field declarations - FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); - index.addDeclaration(node.getNameToken(), fieldEntry); + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { + // treat enum declarations as field declarations + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); + index.addDeclaration(node.getNameToken(), fieldEntry); - return recurse(node, index); - } + return recurse(node, index); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java index 40381f4..a94a55b 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -8,374 +8,374 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + 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.mapping.ClassEntry; public class SourceIndexVisitor implements IAstVisitor { - @Override - public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { - TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getInternalName()); - index.addDeclaration(node.getNameToken(), classEntry); - - return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); - } - - protected Void recurse(AstNode node, SourceIndex index) { - for (final AstNode child : node.getChildren()) { - child.acceptVisitor(this, index); - } - return null; - } - - @Override - public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitSimpleType(SimpleType node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitComment(Comment node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitIdentifier(Identifier node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitBlockStatement(BlockStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitBreakStatement(BreakStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitLabelStatement(LabelStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitSwitchSection(SwitchSection node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitCaseLabel(CaseLabel node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitCatchClause(CatchClause node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitAnnotation(Annotation node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitNewLine(NewLineNode node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitText(TextNode node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitComposedType(ComposedType node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitWhileStatement(WhileStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitCastExpression(CastExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitForStatement(ForStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitGotoStatement(GotoStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitWildcardType(WildcardType node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitAssertStatement(AssertStatement node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { - return recurse(node, index); - } + @Override + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + index.addDeclaration(node.getNameToken(), classEntry); + + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); + } + + protected Void recurse(AstNode node, SourceIndex index) { + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, index); + } + return null; + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitComment(Comment node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIdentifier(Identifier node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBlockStatement(BlockStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBreakStatement(BreakStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLabelStatement(LabelStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSwitchSection(SwitchSection node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCaseLabel(CaseLabel node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCatchClause(CatchClause node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAnnotation(Annotation node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitNewLine(NewLineNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitText(TextNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitComposedType(ComposedType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitWhileStatement(WhileStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCastExpression(CastExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitForStatement(ForStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitGotoStatement(GotoStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitWildcardType(WildcardType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAssertStatement(AssertStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { + return recurse(node, index); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java index 42f4660..266d202 100644 --- a/src/main/java/cuchaz/enigma/analysis/Token.java +++ b/src/main/java/cuchaz/enigma/analysis/Token.java @@ -8,48 +8,48 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; public class Token implements Comparable { - public int start; - public int end; - public String text; - - public Token(int start, int end, String source) { - this.start = start; - this.end = end; - if (source != null) { - this.text = source.substring(start, end); - } - } - - public boolean contains(int pos) { - return pos >= start && pos <= end; - } - - @Override - public int compareTo(Token other) { - return start - other.start; - } - - @Override - public boolean equals(Object other) { - return other instanceof Token && equals((Token) other); - } - - @Override - public int hashCode() - { - return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0); - } - - public boolean equals(Token other) { - return start == other.start && end == other.end; - } - - @Override - public String toString() { - return String.format("[%d,%d]", start, end); - } + public int start; + public int end; + public String text; + + public Token(int start, int end, String source) { + this.start = start; + this.end = end; + if (source != null) { + this.text = source.substring(start, end); + } + } + + public boolean contains(int pos) { + return pos >= start && pos <= end; + } + + @Override + public int compareTo(Token other) { + return start - other.start; + } + + @Override + public boolean equals(Object other) { + return other instanceof Token && equals((Token) other); + } + + @Override + public int hashCode() { + return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0); + } + + public boolean equals(Token other) { + return start == other.start && end == other.end; + } + + @Override + public String toString() { + return String.format("[%d,%d]", start, end); + } } diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java index d51131f..26be05b 100644 --- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java @@ -8,291 +8,288 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + 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 java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - import cuchaz.enigma.mapping.*; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtField; import javassist.bytecode.Descriptor; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + public class TranslationIndex { - private Map superclasses; - private Multimap fieldEntries; - private Multimap behaviorEntries; - private Multimap interfaces; - - public TranslationIndex() { - this.superclasses = Maps.newHashMap(); - this.fieldEntries = HashMultimap.create(); - this.behaviorEntries = HashMultimap.create(); - this.interfaces = HashMultimap.create(); - } - - public TranslationIndex(TranslationIndex other, Translator translator) { - // translate the superclasses - this.superclasses = Maps.newHashMap(); - for (Map.Entry mapEntry : other.superclasses.entrySet()) { - this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); - } - - // translate the interfaces - this.interfaces = HashMultimap.create(); - for (Map.Entry mapEntry : other.interfaces.entries()) { - this.interfaces.put( - translator.translateEntry(mapEntry.getKey()), - translator.translateEntry(mapEntry.getValue()) - ); - } - - // translate the fields - this.fieldEntries = HashMultimap.create(); - for (Map.Entry mapEntry : other.fieldEntries.entries()) { - this.fieldEntries.put( - translator.translateEntry(mapEntry.getKey()), - translator.translateEntry(mapEntry.getValue()) - ); - } - - this.behaviorEntries = HashMultimap.create(); - for (Map.Entry mapEntry : other.behaviorEntries.entries()) { - this.behaviorEntries.put( - translator.translateEntry(mapEntry.getKey()), - translator.translateEntry(mapEntry.getValue()) - ); - } - } - - public void indexClass(CtClass c) { - indexClass(c, true); - } - - public void indexClass(CtClass c, boolean indexMembers) { - ClassEntry classEntry = EntryFactory.getClassEntry(c); - if (isJre(classEntry)) { - return; - } - - // add the superclass - ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); - if (superclassEntry != null) { - this.superclasses.put(classEntry, superclassEntry); - } - - // add the interfaces - for (String interfaceClassName : c.getClassFile().getInterfaces()) { - ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); - if (!isJre(interfaceClassEntry)) { - - this.interfaces.put(classEntry, interfaceClassEntry); - } - } - - if (indexMembers) { - // add fields - for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); - this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); - } - - // add behaviors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); - } - } - } - - public void renameClasses(Map renames) { - EntryRenamer.renameClassesInMap(renames, this.superclasses); - EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); - EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); - } - - 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 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 instanceof FieldEntry) { - return fieldExists((FieldEntry) entry); - } else if (entry instanceof BehaviorEntry) { - return behaviorExists((BehaviorEntry) entry); - } else if (entry instanceof ArgumentEntry) { - return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); - } else if (entry instanceof LocalVariableEntry) { - return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); - } - throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); - } - - public boolean fieldExists(FieldEntry fieldEntry) { - return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); - } - - public boolean behaviorExists(BehaviorEntry behaviorEntry) { - return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); - } - - public ClassEntry resolveEntryClass(Entry entry) { - return resolveEntryClass(entry, false); - } - - public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { - if (entry instanceof ClassEntry) { - return (ClassEntry) entry; - } - - ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild); - if (superclassEntry != null) { - return superclassEntry; - } - - ClassEntry interfaceEntry = resolveInterface(entry); - if (interfaceEntry != null) { - return interfaceEntry; - } - - return null; - } - - public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) { - - // Default case - if (!checkSuperclassBeforeChild) - return resolveSuperclass(entry); - - // Save the original entry - Entry originalEntry = entry; - - // Get all possible superclasses and reverse the list - List superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); - - boolean existInEntry = false; - - for (ClassEntry classEntry : superclasses) - { - entry = entry.cloneToNewClass(classEntry); - existInEntry = entryExists(entry); - - // Check for possible entry in interfaces of superclasses - ClassEntry interfaceEntry = resolveInterface(entry); - if (interfaceEntry != null) - return interfaceEntry; - if (existInEntry) - break; - } - - // Doesn't exists in superclasses? check the child or return null - if (!existInEntry) - return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); - - return entry.getClassEntry(); - } - - public ClassEntry resolveSuperclass(Entry entry) - { - // this entry could refer to a method on a class where the method is not actually implemented - // travel up the inheritance tree to find the closest implementation - - while (!entryExists(entry)) { - // is there a parent class? - ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); - if (superclassEntry == null) { - // this is probably a method from a class in a library - // we can't trace the implementation up any higher unless we index the library - return null; - } - - // move up to the parent class - entry = entry.cloneToNewClass(superclassEntry); - } - return entry.getClassEntry(); - } - - public ClassEntry resolveInterface(Entry entry) { - // the interfaces for any class is a forest - // so let's look at all the trees - - for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { - Collection subInterface = this.interfaces.get(interfaceEntry); - if (subInterface != null && !subInterface.isEmpty()) - { - ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); - if (result != null) - return result; - } - ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); - if (resolvedClassEntry != null) { - return resolvedClassEntry; - } - } - return null; - } - - private boolean isJre(ClassEntry classEntry) { - String packageName = classEntry.getPackageName(); - return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); - } + private Map superclasses; + private Multimap fieldEntries; + private Multimap behaviorEntries; + private Multimap interfaces; + + public TranslationIndex() { + this.superclasses = Maps.newHashMap(); + this.fieldEntries = HashMultimap.create(); + this.behaviorEntries = HashMultimap.create(); + this.interfaces = HashMultimap.create(); + } + + public TranslationIndex(TranslationIndex other, Translator translator) { + // translate the superclasses + this.superclasses = Maps.newHashMap(); + for (Map.Entry mapEntry : other.superclasses.entrySet()) { + this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); + } + + // translate the interfaces + this.interfaces = HashMultimap.create(); + for (Map.Entry mapEntry : other.interfaces.entries()) { + this.interfaces.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + // translate the fields + this.fieldEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.fieldEntries.entries()) { + this.fieldEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + this.behaviorEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.behaviorEntries.entries()) { + this.behaviorEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + } + + public void indexClass(CtClass c) { + indexClass(c, true); + } + + public void indexClass(CtClass c, boolean indexMembers) { + ClassEntry classEntry = EntryFactory.getClassEntry(c); + if (isJre(classEntry)) { + return; + } + + // add the superclass + ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); + if (superclassEntry != null) { + this.superclasses.put(classEntry, superclassEntry); + } + + // add the interfaces + for (String interfaceClassName : c.getClassFile().getInterfaces()) { + ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); + if (!isJre(interfaceClassEntry)) { + + this.interfaces.put(classEntry, interfaceClassEntry); + } + } + + if (indexMembers) { + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); + this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } + } + } + + public void renameClasses(Map renames) { + EntryRenamer.renameClassesInMap(renames, this.superclasses); + EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); + EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); + } + + 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 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 instanceof FieldEntry) { + return fieldExists((FieldEntry) entry); + } else if (entry instanceof BehaviorEntry) { + return behaviorExists((BehaviorEntry) entry); + } else if (entry instanceof ArgumentEntry) { + return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); + } else if (entry instanceof LocalVariableEntry) { + return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); + } + throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); + } + + public boolean fieldExists(FieldEntry fieldEntry) { + return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); + } + + public boolean behaviorExists(BehaviorEntry behaviorEntry) { + return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); + } + + public ClassEntry resolveEntryClass(Entry entry) { + return resolveEntryClass(entry, false); + } + + public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { + if (entry instanceof ClassEntry) { + return (ClassEntry) entry; + } + + ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild); + if (superclassEntry != null) { + return superclassEntry; + } + + ClassEntry interfaceEntry = resolveInterface(entry); + if (interfaceEntry != null) { + return interfaceEntry; + } + + return null; + } + + public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) { + + // Default case + if (!checkSuperclassBeforeChild) + return resolveSuperclass(entry); + + // Save the original entry + Entry originalEntry = entry; + + // Get all possible superclasses and reverse the list + List superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); + + boolean existInEntry = false; + + for (ClassEntry classEntry : superclasses) { + entry = entry.cloneToNewClass(classEntry); + existInEntry = entryExists(entry); + + // Check for possible entry in interfaces of superclasses + ClassEntry interfaceEntry = resolveInterface(entry); + if (interfaceEntry != null) + return interfaceEntry; + if (existInEntry) + break; + } + + // Doesn't exists in superclasses? check the child or return null + if (!existInEntry) + return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); + + return entry.getClassEntry(); + } + + public ClassEntry resolveSuperclass(Entry entry) { + // this entry could refer to a method on a class where the method is not actually implemented + // travel up the inheritance tree to find the closest implementation + + while (!entryExists(entry)) { + // is there a parent class? + ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); + if (superclassEntry == null) { + // this is probably a method from a class in a library + // we can't trace the implementation up any higher unless we index the library + return null; + } + + // move up to the parent class + entry = entry.cloneToNewClass(superclassEntry); + } + return entry.getClassEntry(); + } + + public ClassEntry resolveInterface(Entry entry) { + // the interfaces for any class is a forest + // so let's look at all the trees + + for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { + Collection subInterface = this.interfaces.get(interfaceEntry); + if (subInterface != null && !subInterface.isEmpty()) { + ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); + if (result != null) + return result; + } + ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); + if (resolvedClassEntry != null) { + return resolvedClassEntry; + } + } + 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/TreeDumpVisitor.java b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java index cc025da..c98fb9e 100644 --- a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.analysis; import com.strobel.componentmodel.Key; @@ -19,420 +20,420 @@ import java.nio.charset.Charset; public class TreeDumpVisitor implements IAstVisitor { - private File file; - private Writer out; - - public TreeDumpVisitor(File file) { - this.file = file; - } - - @Override - public Void visitCompilationUnit(CompilationUnit node, Void ignored) { - try { - out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); - recurse(node, ignored); - out.close(); - return null; - } catch (IOException ex) { - throw new Error(ex); - } - } - - private Void recurse(AstNode node, Void ignored) { - // show the tree - try { - out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); - } catch (IOException ex) { - throw new Error(ex); - } - - // recurse - for (final AstNode child : node.getChildren()) { - child.acceptVisitor(this, ignored); - } - return null; - } - - private String getText(AstNode node) { - if (node instanceof Identifier) { - return "\"" + ((Identifier) node).getName() + "\""; - } - return ""; - } - - private String dumpUserData(AstNode node) { - StringBuilder buf = new StringBuilder(); - for (Key key : Keys.ALL_KEYS) { - Object val = node.getUserData(key); - if (val != null) { - buf.append(String.format(" [%s=%s]", key, val)); - } - } - return buf.toString(); - } - - private String getIndent(AstNode node) { - StringBuilder buf = new StringBuilder(); - int depth = getDepth(node); - for (int i = 0; i < depth; i++) { - buf.append("\t"); - } - return buf.toString(); - } - - private int getDepth(AstNode node) { - int depth = -1; - while (node != null) { - depth++; - node = node.getParent(); - } - return depth; - } - - // OVERRIDES WE DON'T CARE ABOUT - - @Override - public Void visitInvocationExpression(InvocationExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitSimpleType(SimpleType node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitComment(Comment node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitIdentifier(Identifier node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitBlockStatement(BlockStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitBreakStatement(BreakStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitContinueStatement(ContinueStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitEmptyStatement(EmptyStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitIfElseStatement(IfElseStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitLabelStatement(LabelStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitLabeledStatement(LabeledStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitReturnStatement(ReturnStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitSwitchStatement(SwitchStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitSwitchSection(SwitchSection node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitCaseLabel(CaseLabel node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitThrowStatement(ThrowStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitCatchClause(CatchClause node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitAnnotation(Annotation node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitNewLine(NewLineNode node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitVariableInitializer(VariableInitializer node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitText(TextNode node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitComposedType(ComposedType node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitWhileStatement(WhileStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitCastExpression(CastExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitIndexerExpression(IndexerExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitForStatement(ForStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitForEachStatement(ForEachStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitGotoStatement(GotoStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitWildcardType(WildcardType node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitAssertStatement(AssertStatement node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitLambdaExpression(LambdaExpression node, Void ignored) { - return recurse(node, ignored); - } - - @Override - public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { - return recurse(node, ignored); - } + private File file; + private Writer out; + + public TreeDumpVisitor(File file) { + this.file = file; + } + + @Override + public Void visitCompilationUnit(CompilationUnit node, Void ignored) { + try { + out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); + recurse(node, ignored); + out.close(); + return null; + } catch (IOException ex) { + throw new Error(ex); + } + } + + private Void recurse(AstNode node, Void ignored) { + // show the tree + try { + out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); + } catch (IOException ex) { + throw new Error(ex); + } + + // recurse + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, ignored); + } + return null; + } + + private String getText(AstNode node) { + if (node instanceof Identifier) { + return "\"" + ((Identifier) node).getName() + "\""; + } + return ""; + } + + private String dumpUserData(AstNode node) { + StringBuilder buf = new StringBuilder(); + for (Key key : Keys.ALL_KEYS) { + Object val = node.getUserData(key); + if (val != null) { + buf.append(String.format(" [%s=%s]", key, val)); + } + } + return buf.toString(); + } + + private String getIndent(AstNode node) { + StringBuilder buf = new StringBuilder(); + int depth = getDepth(node); + for (int i = 0; i < depth; i++) { + buf.append("\t"); + } + return buf.toString(); + } + + private int getDepth(AstNode node) { + int depth = -1; + while (node != null) { + depth++; + node = node.getParent(); + } + return depth; + } + + // OVERRIDES WE DON'T CARE ABOUT + + @Override + public Void visitInvocationExpression(InvocationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSimpleType(SimpleType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitComment(Comment node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIdentifier(Identifier node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBlockStatement(BlockStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBreakStatement(BreakStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitContinueStatement(ContinueStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitEmptyStatement(EmptyStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIfElseStatement(IfElseStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLabelStatement(LabelStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLabeledStatement(LabeledStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitReturnStatement(ReturnStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSwitchStatement(SwitchStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSwitchSection(SwitchSection node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCaseLabel(CaseLabel node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitThrowStatement(ThrowStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCatchClause(CatchClause node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAnnotation(Annotation node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitNewLine(NewLineNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitVariableInitializer(VariableInitializer node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitText(TextNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitComposedType(ComposedType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitWhileStatement(WhileStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCastExpression(CastExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIndexerExpression(IndexerExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitForStatement(ForStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitGotoStatement(GotoStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitWildcardType(WildcardType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAssertStatement(AssertStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLambdaExpression(LambdaExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { + return recurse(node, ignored); + } } -- cgit v1.2.3