From ed9b5cdfc648e86fd463bfa8d86b94c41671e14c Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Feb 2015 21:29:25 -0500 Subject: switch all classes to new signature/type system --- src/cuchaz/enigma/analysis/Access.java | 43 ++ .../enigma/analysis/BehaviorReferenceTreeNode.java | 93 +++ .../analysis/ClassImplementationsTreeNode.java | 80 +++ .../enigma/analysis/ClassInheritanceTreeNode.java | 85 +++ src/cuchaz/enigma/analysis/EntryReference.java | 126 ++++ src/cuchaz/enigma/analysis/EntryRenamer.java | 171 +++++ .../enigma/analysis/FieldReferenceTreeNode.java | 81 +++ src/cuchaz/enigma/analysis/JarClassIterator.java | 137 ++++ src/cuchaz/enigma/analysis/JarIndex.java | 734 +++++++++++++++++++++ .../analysis/MethodImplementationsTreeNode.java | 100 +++ .../enigma/analysis/MethodInheritanceTreeNode.java | 114 ++++ src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 18 + src/cuchaz/enigma/analysis/SourceIndex.java | 173 +++++ .../analysis/SourceIndexBehaviorVisitor.java | 164 +++++ .../enigma/analysis/SourceIndexClassVisitor.java | 115 ++++ src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 452 +++++++++++++ src/cuchaz/enigma/analysis/Token.java | 56 ++ src/cuchaz/enigma/analysis/TranslationIndex.java | 227 +++++++ src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 512 ++++++++++++++ 19 files changed, 3481 insertions(+) create mode 100644 src/cuchaz/enigma/analysis/Access.java create mode 100644 src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/EntryReference.java create mode 100644 src/cuchaz/enigma/analysis/EntryRenamer.java create mode 100644 src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/JarClassIterator.java create mode 100644 src/cuchaz/enigma/analysis/JarIndex.java create mode 100644 src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndex.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexVisitor.java create mode 100644 src/cuchaz/enigma/analysis/Token.java create mode 100644 src/cuchaz/enigma/analysis/TranslationIndex.java create mode 100644 src/cuchaz/enigma/analysis/TreeDumpVisitor.java (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java new file mode 100644 index 0000000..8d3409a --- /dev/null +++ b/src/cuchaz/enigma/analysis/Access.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.lang.reflect.Modifier; + +import javassist.CtBehavior; +import javassist.CtField; + +public enum Access { + + Public, + Protected, + 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) { + if (Modifier.isPublic(modifiers)) { + return Public; + } else if (Modifier.isProtected(modifiers)) { + return Protected; + } else if (Modifier.isPrivate(modifiers)) { + return Private; + } + // assume public by default + return Public; + } +} diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java new file mode 100644 index 0000000..9adac5e --- /dev/null +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Set; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; + +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.Translator; + +public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + + private static final long serialVersionUID = -3658163700783307520L; + + private Translator m_deobfuscatingTranslator; + private BehaviorEntry m_entry; + private EntryReference m_reference; + private Access m_access; + + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + m_access = access; + } + + @Override + public BehaviorEntry getEntry() { + return m_entry; + } + + @Override + public EntryReference getReference() { + return m_reference; + } + + @Override + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); + } + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + for (EntryReference reference : index.getBehaviorReferences(m_entry)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); + } + + if (recurse && children != null) { + for (Object child : children) { + if (child instanceof BehaviorReferenceTreeNode) { + BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode)child; + + // don't recurse into ancestor + Set ancestors = Sets.newHashSet(); + TreeNode n = (TreeNode)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); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java new file mode 100644 index 0000000..49aac5f --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 3112703459157851912L; + + private Translator m_deobfuscatingTranslator; + private ClassEntry m_entry; + + public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public ClassEntry getClassEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + return className; + } + + public void load(JarIndex index) { + // get all method implementations + List nodes = Lists.newArrayList(); + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { + nodes.add(new ClassImplementationsTreeNode(m_deobfuscatingTranslator, new ClassEntry(implementingClassName))); + } + + // add them to this node + for (ClassImplementationsTreeNode node : nodes) { + this.add(node); + } + } + + public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { + // is this the node? + if (node.m_entry.equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java new file mode 100644 index 0000000..3eaa391 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 4432367405826178490L; + + private Translator m_deobfuscatingTranslator; + private String m_obfClassName; + + public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_obfClassName = obfClassName; + } + + public String getObfClassName() { + return m_obfClassName; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_obfClassName); + } + + @Override + public String toString() { + String deobfClassName = getDeobfClassName(); + if (deobfClassName != null) { + return deobfClassName; + } + return m_obfClassName; + } + + public void load(TranslationIndex ancestries, boolean recurse) { + // get all the child nodes + List nodes = Lists.newArrayList(); + for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(m_obfClassName))) { + nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassEntry.getName())); + } + + // add them to this node + for (ClassInheritanceTreeNode node : nodes) { + this.add(node); + } + + if (recurse) { + for (ClassInheritanceTreeNode node : nodes) { + node.load(ancestries, true); + } + } + } + + public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { + // is this the node? + if (node.getObfClassName().equals(entry.getName())) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java new file mode 100644 index 0000000..bb611df --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Arrays; +import java.util.List; + +import cuchaz.enigma.Util; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; + +public class EntryReference { + + private static final List ConstructorNonNames = Arrays.asList("this", "super", "static"); + public E entry; + public C context; + + private boolean m_isNamed; + + 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; + + m_isNamed = sourceName != null && sourceName.length() > 0; + if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { + m_isNamed = false; + } + } + + public EntryReference(E entry, C context, EntryReference other) { + this.entry = entry; + this.context = context; + m_isNamed = other.m_isNamed; + } + + public ClassEntry getLocationClassEntry() { + if (context != null) { + return context.getClassEntry(); + } + return entry.getClassEntry(); + } + + public boolean isNamed() { + return m_isNamed; + } + + 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.getInnerClassName(); + } + } + + return getNameableEntry().getName(); + } + + @Override + public int hashCode() { + if (context != null) { + return Util.combineHashesOrdered(entry.hashCode(), context.hashCode()); + } + return entry.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof EntryReference) { + return equals((EntryReference)other); + } + return false; + } + + 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/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java new file mode 100644 index 0000000..b54489c --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.AbstractMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryRenamer { + + public static void renameClassesInSet(Map renames, Set set) { + List entries = Lists.newArrayList(); + for (T val : set) { + entries.add(renameClassesInThing(renames, val)); + } + set.clear(); + set.addAll(entries); + } + + public static void renameClassesInMap(Map renames, Map map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameClassesInMultimap(Map renames, Multimap map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameMethodsInMultimap(Map renames, Multimap map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameMethodsInMap(Map renames, Map map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + public static T renameMethodsInThing(Map renames, T thing) { + if (thing instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)thing; + MethodEntry newMethodEntry = renames.get(methodEntry); + if (newMethodEntry != null) { + return (T)new MethodEntry( + methodEntry.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(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()); + } else if (thing instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry)thing; + return (T)new ConstructorEntry( + renameClassesInThing(renames, constructorEntry.getClassEntry()), + constructorEntry.getSignature() + ); + } else if (thing instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)thing; + return (T)new MethodEntry( + renameClassesInThing(renames, methodEntry.getClassEntry()), + methodEntry.getName(), + 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; + } + + return thing; + } +} diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java new file mode 100644 index 0000000..2173eea --- /dev/null +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Translator; + +public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + + private static final long serialVersionUID = -7934108091928699835L; + + private Translator m_deobfuscatingTranslator; + private FieldEntry m_entry; + private EntryReference m_reference; + private Access m_access; + + public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + m_access = access; + } + + @Override + public FieldEntry getEntry() { + return m_entry; + } + + @Override + public EntryReference getReference() { + return m_reference; + } + + @Override + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); + } + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + if (m_reference == null) { + for (EntryReference reference : index.getFieldReferences(m_entry)) { + add(new FieldReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); + } + } else { + for (EntryReference reference : index.getBehaviorReferences(m_reference.context)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_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); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java new file mode 100644 index 0000000..72a9912 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; + +public class JarClassIterator implements Iterator { + + private JarFile m_jar; + private Iterator m_iter; + + public JarClassIterator(JarFile jar) { + m_jar = jar; + + // get the jar entries that correspond to classes + List classEntries = Lists.newArrayList(); + Enumeration entries = m_jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if (entry.getName().endsWith(".class")) { + classEntries.add(entry); + } + } + m_iter = classEntries.iterator(); + } + + @Override + public boolean hasNext() { + return m_iter.hasNext(); + } + + @Override + public CtClass next() { + JarEntry entry = m_iter.next(); + try { + return getClass(m_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 Iterable() { + @Override + public Iterator iterator() { + return new JarClassIterator(jar); + } + }; + } + + public static CtClass getClass(JarFile jar, ClassEntry classEntry) { + try { + return getClass(jar, new JarEntry(classEntry.getName() + ".class")); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + classEntry.getName()); + } + } + + 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())); + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java new file mode 100644 index 0000000..3aac8bd --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -0,0 +1,734 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; + +import javassist.CannotCompileException; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; +import javassist.expr.ConstructorCall; +import javassist.expr.ExprEditor; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class JarIndex { + + private Set m_obfClassEntries; + private TranslationIndex m_translationIndex; + private Multimap m_interfaces; + private Map m_access; + private Map m_fieldClasses; // TODO: this will become obsolete! + private Multimap m_methodImplementations; + private Multimap> m_behaviorReferences; + private Multimap> m_fieldReferences; + private Multimap m_innerClasses; + private Map m_outerClasses; + private Map m_anonymousClasses; + + public JarIndex() { + m_obfClassEntries = Sets.newHashSet(); + m_translationIndex = new TranslationIndex(); + m_interfaces = HashMultimap.create(); + m_access = Maps.newHashMap(); + m_fieldClasses = Maps.newHashMap(); + m_methodImplementations = HashMultimap.create(); + m_behaviorReferences = HashMultimap.create(); + m_fieldReferences = HashMultimap.create(); + m_innerClasses = HashMultimap.create(); + m_outerClasses = Maps.newHashMap(); + m_anonymousClasses = Maps.newHashMap(); + } + + public void indexJar(JarFile jar, boolean buildInnerClasses) { + + // step 1: read the class names + for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) { + if (classEntry.isInDefaultPackage()) { + // move out of default package + classEntry = new ClassEntry(Constants.NonePackage + "/" + classEntry.getName()); + } + m_obfClassEntries.add(classEntry); + } + + // step 2: index field/method/constructor access + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + for (CtField field : c.getDeclaredFields()) { + m_access.put(JavassistUtil.getFieldEntry(field), Access.get(field)); + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + m_access.put(JavassistUtil.getBehaviorEntry(behavior), Access.get(behavior)); + } + } + + // step 3: index extends, implements, fields, and methods + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + m_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); + } + m_interfaces.put(className, interfaceName); + } + for (CtField field : c.getDeclaredFields()) { + indexField(field); + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehavior(behavior); + } + } + + // step 4: index field, method, constructor references + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehaviorReferences(behavior); + } + } + + if (buildInnerClasses) { + // step 5: index inner classes and anonymous classes + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + String outerClassName = findOuterClass(c); + if (outerClassName != null) { + String innerClassName = c.getSimpleName(); + m_innerClasses.put(outerClassName, innerClassName); + boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; + assert (innerWasAdded); + + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); + if (enclosingBehavior != null) { + m_anonymousClasses.put(innerClassName, enclosingBehavior); + + // DEBUG + // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + } else { + // DEBUG + // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + } + } + } + + // step 6: update other indices with inner class info + Map renames = Maps.newHashMap(); + for (Map.Entry entry : m_outerClasses.entrySet()) { + renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); + } + EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); + m_translationIndex.renameClasses(renames); + EntryRenamer.renameClassesInMultimap(renames, m_interfaces); + EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); + EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); + EntryRenamer.renameClassesInMap(renames, m_access); + } + } + + private void indexField(CtField field) { + // get the field entry + String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); + FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); + + // is the field a class type? + if (field.getSignature().startsWith("L")) { + ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); + m_fieldClasses.put(fieldEntry, fieldTypeEntry); + } + } + + private void indexBehavior(CtBehavior behavior) { + // get the behavior entry + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + if (behaviorEntry instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)behaviorEntry; + + // index implementation + m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry); + } + // looks like we don't care about constructors here + } + + private void indexBehaviorReferences(CtBehavior behavior) { + // index method calls + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + try { + behavior.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + MethodEntry calledMethodEntry = JavassistUtil.getMethodEntry(call); + ClassEntry resolvedClassEntry = m_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 + ); + m_behaviorReferences.put(calledMethodEntry, reference); + } + + @Override + public void edit(FieldAccess call) { + FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call); + ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { + calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); + } + EntryReference reference = new EntryReference( + calledFieldEntry, + call.getFieldName(), + behaviorEntry + ); + m_fieldReferences.put(calledFieldEntry, reference); + } + + @Override + public void edit(ConstructorCall call) { + ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + EntryReference reference = new EntryReference( + calledConstructorEntry, + call.getMethodName(), + behaviorEntry + ); + m_behaviorReferences.put(calledConstructorEntry, reference); + } + + @Override + public void edit(NewExpr call) { + ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + EntryReference reference = new EntryReference( + calledConstructorEntry, + call.getClassName(), + behaviorEntry + ); + m_behaviorReferences.put(calledConstructorEntry, reference); + } + }); + } catch (CannotCompileException ex) { + throw new Error(ex); + } + } + + private String findOuterClass(CtClass c) { + + // 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; + } + + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + ConstructorEntry constructorEntry = JavassistUtil.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 = m_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().getName(); + } 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().getName(); + } 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().getName(); + } 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? + if (!m_obfClassEntries.contains(outerClassEntry)) { + return false; + } + + return true; + } + + @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, String outerClassName) { + + 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 = JavassistUtil.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)) { + ClassEntry fieldClass = getFieldClass(fieldEntry); + if (fieldClass != null && fieldClass.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 m_obfClassEntries; + } + + public TranslationIndex getTranslationIndex() { + return m_translationIndex; + } + + public Access getAccess(Entry entry) { + return m_access.get(entry); + } + + public ClassEntry getFieldClass(FieldEntry fieldEntry) { + return m_fieldClasses.get(fieldEntry); + } + + public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { + + // get the root node + List ancestry = Lists.newArrayList(); + ancestry.add(obfClassEntry.getName()); + for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) { + ancestry.add(classEntry.getName()); + } + ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( + deobfuscatingTranslator, + ancestry.get(ancestry.size() - 1) + ); + + // expand all children recursively + rootNode.load(m_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 : m_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 MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + + MethodEntry interfaceMethodEntry; + + // is this method on an interface? + if (isInterface(obfMethodEntry.getClassName())) { + interfaceMethodEntry = obfMethodEntry; + } else { + // get the interface class + List methodInterfaces = Lists.newArrayList(); + for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + // is this method defined in this interface? + MethodEntry methodInterface = new MethodEntry( + new ClassEntry(interfaceName), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if (containsObfBehavior(methodInterface)) { + methodInterfaces.add(methodInterface); + } + } + if (methodInterfaces.isEmpty()) { + return null; + } + if (methodInterfaces.size() > 1) { + throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!"); + } + interfaceMethodEntry = methodInterfaces.get(0); + } + + MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); + rootNode.load(this); + return rootNode; + } + + public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { + Set methodEntries = Sets.newHashSet(); + getRelatedMethodImplementations(methodEntries, getMethodInheritance(null, 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 interface methods too + MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry); + if (implementations != null) { + getRelatedMethodImplementations(methodEntries, implementations); + } + + // 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); + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode)node.getChildAt(i)); + } + } + + public Collection> getFieldReferences(FieldEntry fieldEntry) { + return m_fieldReferences.get(fieldEntry); + } + + public Collection getReferencedFields(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set fieldEntries = Sets.newHashSet(); + for (EntryReference reference : m_fieldReferences.values()) { + if (reference.context == behaviorEntry) { + fieldEntries.add(reference.entry); + } + } + return fieldEntries; + } + + public Collection> getBehaviorReferences(BehaviorEntry behaviorEntry) { + return m_behaviorReferences.get(behaviorEntry); + } + + public Collection getReferencedBehaviors(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set behaviorEntries = Sets.newHashSet(); + for (EntryReference reference : m_behaviorReferences.values()) { + if (reference.context == behaviorEntry) { + behaviorEntries.add(reference.entry); + } + } + return behaviorEntries; + } + + public Collection getInnerClasses(String obfOuterClassName) { + return m_innerClasses.get(obfOuterClassName); + } + + public String getOuterClass(String obfInnerClassName) { + // make sure we use the right name + if (new ClassEntry(obfInnerClassName).getPackageName() != null) { + throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); + } + return m_outerClasses.get(obfInnerClassName); + } + + public boolean isAnonymousClass(String obfInnerClassName) { + return m_anonymousClasses.containsKey(obfInnerClassName); + } + + public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { + return m_anonymousClasses.get(obfInnerClassName); + } + + public Set getInterfaces(String className) { + Set interfaceNames = new HashSet(); + interfaceNames.addAll(m_interfaces.get(className)); + for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) { + interfaceNames.addAll(m_interfaces.get(ancestor.getName())); + } + return interfaceNames; + } + + public Set getImplementingClasses(String targetInterfaceName) { + // linear search is fast enough for now + Set classNames = Sets.newHashSet(); + for (Map.Entry entry : m_interfaces.entries()) { + String className = entry.getKey(); + String interfaceName = entry.getValue(); + if (interfaceName.equals(targetInterfaceName)) { + classNames.add(className); + m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className)); + } + } + return classNames; + } + + public boolean isInterface(String className) { + return m_interfaces.containsValue(className); + } + + public boolean containsObfClass(ClassEntry obfClassEntry) { + return m_obfClassEntries.contains(obfClassEntry); + } + + public boolean containsObfField(FieldEntry obfFieldEntry) { + return m_access.containsKey(obfFieldEntry); + } + + public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { + return m_access.containsKey(obfBehaviorEntry); + } + + public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { + // check the behavior + if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { + return false; + } + + // check the argument + if (obfArgumentEntry.getIndex() >= obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size()) { + return false; + } + + return true; + } + + public boolean containsObfEntry(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + return containsObfClass((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + return containsObfField((FieldEntry)obfEntry); + } else if (obfEntry instanceof BehaviorEntry) { + return containsObfBehavior((BehaviorEntry)obfEntry); + } else if (obfEntry instanceof ArgumentEntry) { + return containsObfArgument((ArgumentEntry)obfEntry); + } else { + throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); + } + } +} diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java new file mode 100644 index 0000000..1009226 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 3781080657461899915L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + + public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { + if (entry == null) { + throw new IllegalArgumentException("entry cannot be null!"); + } + + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public MethodEntry getMethodEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + public String getDeobfMethodName() { + return m_deobfuscatingTranslator.translate(m_entry); + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + + public void load(JarIndex index) { + // get all method implementations + List nodes = Lists.newArrayList(); + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { + MethodEntry methodEntry = new MethodEntry( + new ClassEntry(implementingClassName), + m_entry.getName(), + m_entry.getSignature() + ); + if (index.containsObfBehavior(methodEntry)) { + nodes.add(new MethodImplementationsTreeNode(m_deobfuscatingTranslator, methodEntry)); + } + } + + // add them to this node + for (MethodImplementationsTreeNode node : nodes) { + this.add(node); + } + } + + 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; + } +} diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java new file mode 100644 index 0000000..8718220 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 1096677030991810007L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + private boolean m_isImplemented; + + public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_isImplemented = isImplemented; + } + + public MethodEntry getMethodEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + public String getDeobfMethodName() { + return m_deobfuscatingTranslator.translate(m_entry); + } + + public boolean isImplemented() { + return m_isImplemented; + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + + if (!m_isImplemented) { + return className; + } else { + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = m_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(m_entry.getClassEntry())) { + MethodEntry methodEntry = new MethodEntry( + subclassEntry, + m_entry.getName(), + m_entry.getSignature() + ); + nodes.add(new MethodInheritanceTreeNode( + m_deobfuscatingTranslator, + methodEntry, + index.containsObfBehavior(methodEntry) + )); + } + + // add them to this node + for (MethodInheritanceTreeNode node : nodes) { + this.add(node); + } + + 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; + } +} diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java new file mode 100644 index 0000000..2b08616 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import cuchaz.enigma.mapping.Entry; + +public interface ReferenceTreeNode { + E getEntry(); + EntryReference getReference(); +} diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java new file mode 100644 index 0000000..b43ab61 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +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; + +public class SourceIndex { + + private String m_source; + private TreeMap> m_tokenToReference; + private Multimap,Token> m_referenceToTokens; + private Map m_declarationToToken; + private List m_lineOffsets; + + public SourceIndex(String source) { + m_source = source; + m_tokenToReference = Maps.newTreeMap(); + m_referenceToTokens = HashMultimap.create(); + m_declarationToToken = Maps.newHashMap(); + m_lineOffsets = Lists.newArrayList(); + + // count the lines + m_lineOffsets.add(0); + for (int i = 0; i < source.length(); i++) { + if (source.charAt(i) == '\n') { + m_lineOffsets.add(i + 1); + } + } + } + + public String getSource() { + return m_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()), + m_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 ) ); + + // for tokens representing inner classes, make sure we only get the simple name + int pos = name.lastIndexOf('$'); + if (pos >= 0) { + token.end -= pos + 1; + } + + 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); + m_tokenToReference.put(token, deobfReference); + m_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); + m_tokenToReference.put(token, reference); + m_referenceToTokens.put(reference, token); + m_declarationToToken.put(deobfEntry, token); + } + } + + public Token getReferenceToken(int pos) { + Token token = m_tokenToReference.floorKey(new Token(pos, pos, null)); + if (token != null && token.contains(pos)) { + return token; + } + return null; + } + + public Collection getReferenceTokens(EntryReference deobfReference) { + return m_referenceToTokens.get(deobfReference); + } + + public EntryReference getDeobfReference(Token token) { + if (token == null) { + return null; + } + return m_tokenToReference.get(token); + } + + public void replaceDeobfReference(Token token, EntryReference newDeobfReference) { + EntryReference oldDeobfReference = m_tokenToReference.get(token); + m_tokenToReference.put(token, newDeobfReference); + Collection tokens = m_referenceToTokens.get(oldDeobfReference); + m_referenceToTokens.removeAll(oldDeobfReference); + m_referenceToTokens.putAll(newDeobfReference, tokens); + } + + public Iterable referenceTokens() { + return m_tokenToReference.keySet(); + } + + public Iterable declarationTokens() { + return m_declarationToToken.values(); + } + + public Token getDeclarationToken(Entry deobfEntry) { + return m_declarationToToken.get(deobfEntry); + } + + public int getLineNumber(int pos) { + // line number is 1-based + int line = 0; + for (Integer offset : m_lineOffsets) { + if (offset > pos) { + break; + } + line++; + } + return line; + } + + public int getColumnNumber(int pos) { + // column number is 1-based + return pos - m_lineOffsets.get(getLineNumber(pos) - 1) + 1; + } + + private int toPos(int line, int col) { + // line and col are 1-based + return m_lineOffsets.get(line - 1) + col - 1; + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java new file mode 100644 index 0000000..4155128 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; +import com.strobel.assembler.metadata.ParameterDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; + +public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { + + private BehaviorEntry m_behaviorEntry; + + public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { + m_behaviorEntry = behaviorEntry; + } + + @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 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.getSignature())); + } else if (methodRef.isTypeInitializer()) { + behaviorEntry = new ConstructorEntry(classEntry); + } else { + behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getSignature())); + } + } + 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, m_behaviorEntry); + } + } + + 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.getSignature().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()); + index.addReference(node.getMemberNameToken(), fieldEntry, m_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, m_behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + BehaviorEntry behaviorEntry; + if (methodDef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, new Signature(methodDef.getSignature())); + } else { + behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), new Signature(methodDef.getSignature())); + } + ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); + index.addDeclaration(node.getNameToken(), 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()); + index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); + } + + return recurse(node, index); + } + + @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.getSignature())); + if (node.getType() instanceof SimpleType) { + SimpleType simpleTypeNode = (SimpleType)node.getType(); + index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry); + } + } + + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java new file mode 100644 index 0000000..7222035 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Signature; + +public class SourceIndexClassVisitor extends SourceIndexVisitor { + + private ClassEntry m_classEntry; + + public SourceIndexClassVisitor(ClassEntry classEntry) { + m_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(m_classEntry)) { + // it's a sub-type, recurse + index.addDeclaration(node.getNameToken(), classEntry); + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), 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, m_classEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature()); + AstNode tokenNode = node.getNameToken(); + if (behaviorEntry instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry; + if (constructorEntry.isStatic()) { + 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); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(def.getSignature())); + 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); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + assert (node.getVariables().size() == 1); + VariableInitializer variable = node.getVariables().firstOrNullObject(); + index.addDeclaration(variable.getNameToken(), fieldEntry); + + 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); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + index.addDeclaration(node.getNameToken(), fieldEntry); + + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java new file mode 100644 index 0000000..0d5bdc0 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -0,0 +1,452 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +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); + } +} diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java new file mode 100644 index 0000000..481d2f4 --- /dev/null +++ b/src/cuchaz/enigma/analysis/Token.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * 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) { + this(start, end, null); + } + + 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) { + if (other instanceof Token) { + return equals((Token)other); + } + return false; + } + + 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/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java new file mode 100644 index 0000000..7597c3a --- /dev/null +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.Translator; + +public class TranslationIndex implements Serializable { + + private static final long serialVersionUID = 738687982126844179L; + + private Map m_superclasses; + private Multimap m_fieldEntries; + private Multimap m_behaviorEntries; + + public TranslationIndex() { + m_superclasses = Maps.newHashMap(); + m_fieldEntries = HashMultimap.create(); + m_behaviorEntries = HashMultimap.create(); + } + + public TranslationIndex(TranslationIndex other, Translator translator) { + + // translate the superclasses + m_superclasses = Maps.newHashMap(); + for (Map.Entry mapEntry : other.m_superclasses.entrySet()) { + m_superclasses.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + // translate the fields + m_fieldEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_fieldEntries.entries()) { + m_fieldEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + m_behaviorEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_behaviorEntries.entries()) { + m_behaviorEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + } + + public void indexClass(CtClass c) { + + ClassEntry classEntry = JavassistUtil.getClassEntry(c); + + // add the superclass + ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { + m_superclasses.put(classEntry, superclassEntry); + } + + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } + } + + public void renameClasses(Map renames) { + EntryRenamer.renameClassesInMap(renames, m_superclasses); + EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries); + } + + public ClassEntry getSuperclass(ClassEntry classEntry) { + return m_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 : m_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 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()); + } + throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); + } + + public boolean fieldExists(FieldEntry fieldEntry) { + return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); + } + + public boolean behaviorExists(BehaviorEntry behaviorEntry) { + return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); + } + + public ClassEntry resolveEntryClass(Entry entry) { + + if (entry instanceof ClassEntry) { + return (ClassEntry)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(); + } + + private boolean isJre(ClassEntry classEntry) { + String packageName = classEntry.getPackageName(); + return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); + } + + public void write(OutputStream out) + throws IOException { + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(m_superclasses); + oout.writeObject(m_fieldEntries); + oout.writeObject(m_behaviorEntries); + gzipout.finish(); + } + + @SuppressWarnings("unchecked") + public void read(InputStream in) + throws IOException { + try { + ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in)); + m_superclasses = (HashMap)oin.readObject(); + m_fieldEntries = (HashMultimap)oin.readObject(); + m_behaviorEntries = (HashMultimap)oin.readObject(); + } catch (ClassNotFoundException ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java new file mode 100644 index 0000000..23f8089 --- /dev/null +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -0,0 +1,512 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +import com.strobel.componentmodel.Key; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +public class TreeDumpVisitor implements IAstVisitor { + + private File m_file; + private Writer m_out; + + public TreeDumpVisitor(File file) { + m_file = file; + m_out = null; + } + + @Override + public Void visitCompilationUnit(CompilationUnit node, Void ignored) { + try { + m_out = new FileWriter(m_file); + recurse(node, ignored); + m_out.close(); + return null; + } catch (IOException ex) { + throw new Error(ex); + } + } + + private Void recurse(AstNode node, Void ignored) { + // show the tree + try { + m_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 From 31a1a418b04cd3e7b06cb50cb8674a2c25079f6c Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Feb 2015 23:10:26 -0500 Subject: added types to fields --- src/cuchaz/enigma/analysis/EntryRenamer.java | 29 +++++++++++++++++++--- src/cuchaz/enigma/analysis/JarIndex.java | 26 ++----------------- .../analysis/SourceIndexBehaviorVisitor.java | 5 ++-- .../enigma/analysis/SourceIndexClassVisitor.java | 5 ++-- 4 files changed, 33 insertions(+), 32 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index b54489c..2f27049 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -21,10 +21,13 @@ import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class EntryRenamer { @@ -127,7 +130,7 @@ public class EntryRenamer { } @SuppressWarnings("unchecked") - public static T renameClassesInThing(Map renames, T thing) { + public static T renameClassesInThing(final Map renames, T thing) { if (thing instanceof String) { String stringEntry = (String)thing; if (renames.containsKey(stringEntry)) { @@ -138,19 +141,23 @@ public class EntryRenamer { 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()); + 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()), - constructorEntry.getSignature() + renameClassesInThing(renames, constructorEntry.getSignature()) ); } else if (thing instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)thing; return (T)new MethodEntry( renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), - methodEntry.getSignature() + renameClassesInThing(renames, methodEntry.getSignature()) ); } else if (thing instanceof ArgumentEntry) { ArgumentEntry argumentEntry = (ArgumentEntry)thing; @@ -164,6 +171,20 @@ public class EntryRenamer { 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, new ClassNameReplacer() { + @Override + public String replace(String className) { + return renameClassesInThing(renames, className); + } + }); + } else if (thing instanceof Type) { + return (T)new Type((Type)thing, new ClassNameReplacer() { + @Override + public String replace(String className) { + return renameClassesInThing(renames, className); + } + }); } return thing; diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 3aac8bd..f54beda 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -57,7 +57,6 @@ public class JarIndex { private TranslationIndex m_translationIndex; private Multimap m_interfaces; private Map m_access; - private Map m_fieldClasses; // TODO: this will become obsolete! private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; @@ -70,7 +69,6 @@ public class JarIndex { m_translationIndex = new TranslationIndex(); m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); - m_fieldClasses = Maps.newHashMap(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); @@ -114,9 +112,6 @@ public class JarIndex { } m_interfaces.put(className, interfaceName); } - for (CtField field : c.getDeclaredFields()) { - indexField(field); - } for (CtBehavior behavior : c.getDeclaredBehaviors()) { indexBehavior(behavior); } @@ -169,18 +164,6 @@ public class JarIndex { } } - private void indexField(CtField field) { - // get the field entry - String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); - FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); - - // is the field a class type? - if (field.getSignature().startsWith("L")) { - ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); - m_fieldClasses.put(fieldEntry, fieldTypeEntry); - } - } - private void indexBehavior(CtBehavior behavior) { // get the behavior entry final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); @@ -222,7 +205,7 @@ public class JarIndex { FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { - calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); + calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); } EntryReference reference = new EntryReference( calledFieldEntry, @@ -448,8 +431,7 @@ public class JarIndex { // does the caller use this type? BehaviorEntry caller = references.iterator().next().context; for (FieldEntry fieldEntry : getReferencedFields(caller)) { - ClassEntry fieldClass = getFieldClass(fieldEntry); - if (fieldClass != null && fieldClass.equals(innerClassEntry)) { + if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { // caller references this type, so it can't be anonymous return null; } @@ -475,10 +457,6 @@ public class JarIndex { return m_access.get(entry); } - public ClassEntry getFieldClass(FieldEntry fieldEntry) { - return m_fieldClasses.get(fieldEntry); - } - public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { // get the root node diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index 4155128..f15a724 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -36,6 +36,7 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { @@ -100,7 +101,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { } ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getSignature())); index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry); } @@ -140,7 +141,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); if (ref != null) { ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getSignature())); index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); } diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 7222035..e2ff300 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -31,6 +31,7 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -94,7 +95,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -107,7 +108,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); -- cgit v1.2.3 From 71ec7b53a1b4ecce0623dded1e445818a757b695 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 12:17:26 -0500 Subject: add converter to update old mappings format fix a few decompiler issues too --- src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index f15a724..b4094d9 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -66,11 +66,11 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { if (ref instanceof MethodReference) { MethodReference methodRef = (MethodReference)ref; if (methodRef.isConstructor()) { - behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getSignature())); + 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.getSignature())); + behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); } } if (behaviorEntry != null) { @@ -96,12 +96,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); if (ref != null) { // make sure this is actually a field - if (ref.getSignature().indexOf('(') >= 0) { + 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.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry); } @@ -141,7 +141,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { 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.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); } @@ -153,7 +153,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { 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.getSignature())); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); if (node.getType() instanceof SimpleType) { SimpleType simpleTypeNode = (SimpleType)node.getType(); index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry); -- cgit v1.2.3 From af1041731c8c0ce1846ff7e7b6052ed7327a5dbc Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 22:23:45 -0500 Subject: fix translation issues, particularly with fields --- src/cuchaz/enigma/analysis/JarIndex.java | 57 ++++++------- .../analysis/MethodImplementationsTreeNode.java | 1 + .../enigma/analysis/RelatedMethodChecker.java | 96 ++++++++++++++++++++++ .../enigma/analysis/SourceIndexClassVisitor.java | 11 ++- src/cuchaz/enigma/analysis/TranslationIndex.java | 10 +-- 5 files changed, 134 insertions(+), 41 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/RelatedMethodChecker.java (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index f54beda..24d110e 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -42,12 +42,11 @@ import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; @@ -92,10 +91,10 @@ public class JarIndex { for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); for (CtField field : c.getDeclaredFields()) { - m_access.put(JavassistUtil.getFieldEntry(field), Access.get(field)); + m_access.put(EntryFactory.getFieldEntry(field), Access.get(field)); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - m_access.put(JavassistUtil.getBehaviorEntry(behavior), Access.get(behavior)); + m_access.put(EntryFactory.getBehaviorEntry(behavior), Access.get(behavior)); } } @@ -166,7 +165,7 @@ public class JarIndex { private void indexBehavior(CtBehavior behavior) { // get the behavior entry - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); if (behaviorEntry instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)behaviorEntry; @@ -178,12 +177,12 @@ public class JarIndex { private void indexBehaviorReferences(CtBehavior behavior) { // index method calls - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); try { behavior.instrument(new ExprEditor() { @Override public void edit(MethodCall call) { - MethodEntry calledMethodEntry = JavassistUtil.getMethodEntry(call); + MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { calledMethodEntry = new MethodEntry( @@ -202,7 +201,7 @@ public class JarIndex { @Override public void edit(FieldAccess call) { - FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call); + FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); @@ -217,7 +216,7 @@ public class JarIndex { @Override public void edit(ConstructorCall call) { - ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); EntryReference reference = new EntryReference( calledConstructorEntry, call.getMethodName(), @@ -228,7 +227,7 @@ public class JarIndex { @Override public void edit(NewExpr call) { - ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); EntryReference reference = new EntryReference( calledConstructorEntry, call.getClassName(), @@ -256,7 +255,7 @@ public class JarIndex { } ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); - ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); // gather the classes from the illegally-set synthetic fields Set illegallySetClasses = Sets.newHashSet(); @@ -422,7 +421,7 @@ public class JarIndex { CtConstructor constructor = c.getDeclaredConstructors()[0]; // is this constructor called exactly once? - ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); Collection> references = getBehaviorReferences(constructorEntry); if (references.size() != 1) { return null; @@ -520,17 +519,17 @@ public class JarIndex { return rootNode; } - public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + public List getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { - MethodEntry interfaceMethodEntry; + List interfaceMethodEntries = Lists.newArrayList(); // is this method on an interface? if (isInterface(obfMethodEntry.getClassName())) { - interfaceMethodEntry = obfMethodEntry; + interfaceMethodEntries.add(obfMethodEntry); } else { // get the interface class - List methodInterfaces = Lists.newArrayList(); for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( new ClassEntry(interfaceName), @@ -538,21 +537,20 @@ public class JarIndex { obfMethodEntry.getSignature() ); if (containsObfBehavior(methodInterface)) { - methodInterfaces.add(methodInterface); + interfaceMethodEntries.add(methodInterface); } } - if (methodInterfaces.isEmpty()) { - return null; - } - if (methodInterfaces.size() > 1) { - throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!"); - } - interfaceMethodEntry = methodInterfaces.get(0); } - MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); - rootNode.load(this); - return rootNode; + 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) { @@ -569,9 +567,8 @@ public class JarIndex { } // look at interface methods too - MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry); - if (implementations != null) { - getRelatedMethodImplementations(methodEntries, implementations); + for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(null, methodEntry)) { + getRelatedMethodImplementations(methodEntries, implementationsNode); } // recurse diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 1009226..6cafc55 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -63,6 +63,7 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { } public void load(JarIndex index) { + // get all method implementations List nodes = Lists.newArrayList(); for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { diff --git a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java new file mode 100644 index 0000000..5bd67a0 --- /dev/null +++ b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java @@ -0,0 +1,96 @@ +package cuchaz.enigma.analysis; + +import java.util.Map; +import java.util.Set; + +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; + +public class RelatedMethodChecker { + + private JarIndex m_jarIndex; + private Map,String> m_deobfNamesByGroup; + private Map m_deobfNamesByObfMethod; + private Map> m_groupsByObfMethod; + private Set> m_inconsistentGroups; + + public RelatedMethodChecker(JarIndex jarIndex) { + m_jarIndex = jarIndex; + m_deobfNamesByGroup = Maps.newHashMap(); + m_deobfNamesByObfMethod = Maps.newHashMap(); + m_groupsByObfMethod = Maps.newHashMap(); + m_inconsistentGroups = Sets.newHashSet(); + } + + public void checkMethod(ClassEntry classEntry, MethodMapping methodMapping) { + + // TEMP: disable the expensive check for now, maybe we can optimize it later, or just use it for debugging + if (true) return; + + BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); + if (!(obfBehaviorEntry instanceof MethodEntry)) { + // only methods have related implementations + return; + } + MethodEntry obfMethodEntry = (MethodEntry)obfBehaviorEntry; + String deobfName = methodMapping.getDeobfName(); + m_deobfNamesByObfMethod.put(obfMethodEntry, deobfName); + + // have we seen this method's group before? + Set group = m_groupsByObfMethod.get(obfMethodEntry); + if (group == null) { + + // no, compute the group and save the name + group = m_jarIndex.getRelatedMethodImplementations(obfMethodEntry); + m_deobfNamesByGroup.put(group, deobfName); + + assert(group.contains(obfMethodEntry)); + for (MethodEntry relatedMethodEntry : group) { + m_groupsByObfMethod.put(relatedMethodEntry, group); + } + } + + // check the name + if (!sameName(m_deobfNamesByGroup.get(group), deobfName)) { + m_inconsistentGroups.add(group); + } + } + + private boolean sameName(String a, String b) { + if (a == null && b == null) { + return true; + } else if (a != null && b != null) { + return a.equals(b); + } + return false; + } + + public boolean hasProblems() { + return m_inconsistentGroups.size() > 0; + } + + public String getReport() { + StringBuilder buf = new StringBuilder(); + buf.append(m_inconsistentGroups.size()); + buf.append(" groups of methods related by inheritance and/or interfaces have different deobf names!\n"); + for (Set group : m_inconsistentGroups) { + buf.append("\tGroup with "); + buf.append(group.size()); + buf.append(" methods:\n"); + for (MethodEntry methodEntry : group) { + buf.append("\t\t"); + buf.append(methodEntry.toString()); + buf.append(" => "); + buf.append(m_deobfNamesByObfMethod.get(methodEntry)); + buf.append("\n"); + } + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index e2ff300..d6692f6 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -26,11 +26,10 @@ import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import com.strobel.decompiler.languages.java.ast.VariableInitializer; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Type; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -69,12 +68,13 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature()); + BehaviorEntry behaviorEntry = EntryFactory.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(); } } @@ -85,8 +85,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(def.getSignature())); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(def); index.addDeclaration(node.getNameToken(), constructorEntry); return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); } diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 7597c3a..8651ebd 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -37,7 +37,7 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Translator; public class TranslationIndex implements Serializable { @@ -85,23 +85,23 @@ public class TranslationIndex implements Serializable { public void indexClass(CtClass c) { - ClassEntry classEntry = JavassistUtil.getClassEntry(c); + ClassEntry classEntry = EntryFactory.getClassEntry(c); // add the superclass - ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { m_superclasses.put(classEntry, superclassEntry); } // add fields for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); } // add behaviors for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); } } -- cgit v1.2.3 From ab6de199201f3cb292b986b2803d7d30b1485a47 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 23:31:24 -0500 Subject: work around bad tokens generated by procyon for now --- src/cuchaz/enigma/analysis/SourceIndex.java | 9 +++++---- .../enigma/analysis/SourceIndexBehaviorVisitor.java | 21 ++------------------- .../enigma/analysis/SourceIndexClassVisitor.java | 4 ++-- 3 files changed, 9 insertions(+), 25 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index b43ab61..e31b803 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -82,10 +82,11 @@ public class SourceIndex { // DEBUG // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); - // for tokens representing inner classes, make sure we only get the simple name - int pos = name.lastIndexOf('$'); - if (pos >= 0) { - token.end -= pos + 1; + // if the token has a $ in it, something's wrong. Ignore this token + if (name.lastIndexOf('$') >= 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); + return null; } return token; diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index b4094d9..a9a055b 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -17,12 +17,10 @@ import com.strobel.assembler.metadata.ParameterDefinition; import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.languages.TextLocation; import com.strobel.decompiler.languages.java.ast.AstNode; -import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.IdentifierExpression; import com.strobel.decompiler.languages.java.ast.InvocationExpression; import com.strobel.decompiler.languages.java.ast.Keys; import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; -import com.strobel.decompiler.languages.java.ast.MethodDeclaration; import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; import com.strobel.decompiler.languages.java.ast.SimpleType; @@ -33,6 +31,7 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; @@ -46,16 +45,6 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { m_behaviorEntry = behaviorEntry; } - @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 visitInvocationExpression(InvocationExpression node, SourceIndex index) { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); @@ -122,14 +111,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { @Override public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - BehaviorEntry behaviorEntry; - if (methodDef.isConstructor()) { - behaviorEntry = new ConstructorEntry(classEntry, new Signature(methodDef.getSignature())); - } else { - behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), new Signature(methodDef.getSignature())); - } + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); index.addDeclaration(node.getNameToken(), argumentEntry); diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index d6692f6..f4f4956 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -94,7 +94,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -107,7 +107,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); -- cgit v1.2.3 From 2b3c5c52865b40adfa93910d41738242f17338d4 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Feb 2015 22:10:16 -0500 Subject: add BRIDGE flag to bridge methods --- src/cuchaz/enigma/analysis/BridgeMarker.java | 36 ++++++++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 55 ++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/cuchaz/enigma/analysis/BridgeMarker.java (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/BridgeMarker.java b/src/cuchaz/enigma/analysis/BridgeMarker.java new file mode 100644 index 0000000..e80f87d --- /dev/null +++ b/src/cuchaz/enigma/analysis/BridgeMarker.java @@ -0,0 +1,36 @@ +package cuchaz.enigma.analysis; + +import javassist.CtClass; +import javassist.CtMethod; +import javassist.bytecode.AccessFlag; + +import com.google.common.collect.BiMap; + +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.MethodEntry; + +public class BridgeMarker { + + private BiMap m_bridgedMethods; + + public BridgeMarker(BiMap bridgedMethods) { + m_bridgedMethods = bridgedMethods; + } + + public void markBridges(CtClass c) { + + for (CtMethod method : c.getDeclaredMethods()) { + MethodEntry methodEntry = EntryFactory.getMethodEntry(method); + + // is this a bridge method? + MethodEntry bridgedMethodEntry = m_bridgedMethods.get(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); + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 24d110e..797deb8 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -23,6 +23,8 @@ import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; +import javassist.CtMethod; +import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; import javassist.bytecode.FieldInfo; @@ -32,6 +34,8 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -62,6 +66,7 @@ public class JarIndex { private Multimap m_innerClasses; private Map m_outerClasses; private Map m_anonymousClasses; + private BiMap m_bridgedMethods; public JarIndex() { m_obfClassEntries = Sets.newHashSet(); @@ -74,6 +79,7 @@ public class JarIndex { m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); m_anonymousClasses = Maps.newHashMap(); + m_bridgedMethods = HashBiMap.create(); } public void indexJar(JarFile jar, boolean buildInnerClasses) { @@ -171,6 +177,12 @@ public class JarIndex { // index implementation m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry); + + // look for bridge and bridged methods + CtMethod bridgedMethod = getBridgedMethod((CtMethod)behavior); + if (bridgedMethod != null) { + m_bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); + } } // looks like we don't care about constructors here } @@ -241,6 +253,45 @@ public class JarIndex { } } + 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 String findOuterClass(CtClass c) { // inner classes: @@ -706,4 +757,8 @@ public class JarIndex { throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); } } + + public BiMap getBridgedMethods() { + return m_bridgedMethods; + } } -- cgit v1.2.3 From 1bddb51a8370f96af2dfd61e75d72b155b71923e Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Feb 2015 23:26:33 -0500 Subject: repackage as v0.7b --- src/cuchaz/enigma/analysis/BridgeMarker.java | 11 ++++------- src/cuchaz/enigma/analysis/JarIndex.java | 10 ++++------ 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/BridgeMarker.java b/src/cuchaz/enigma/analysis/BridgeMarker.java index e80f87d..28e3517 100644 --- a/src/cuchaz/enigma/analysis/BridgeMarker.java +++ b/src/cuchaz/enigma/analysis/BridgeMarker.java @@ -3,18 +3,15 @@ package cuchaz.enigma.analysis; import javassist.CtClass; import javassist.CtMethod; import javassist.bytecode.AccessFlag; - -import com.google.common.collect.BiMap; - import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.MethodEntry; public class BridgeMarker { - private BiMap m_bridgedMethods; + private JarIndex m_jarIndex; - public BridgeMarker(BiMap bridgedMethods) { - m_bridgedMethods = bridgedMethods; + public BridgeMarker(JarIndex jarIndex) { + m_jarIndex = jarIndex; } public void markBridges(CtClass c) { @@ -23,7 +20,7 @@ public class BridgeMarker { MethodEntry methodEntry = EntryFactory.getMethodEntry(method); // is this a bridge method? - MethodEntry bridgedMethodEntry = m_bridgedMethods.get(methodEntry); + MethodEntry bridgedMethodEntry = m_jarIndex.getBridgedMethod(methodEntry); if (bridgedMethodEntry != null) { // it's a bridge method! add the bridge flag diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 797deb8..1c74f15 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -34,8 +34,6 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -66,7 +64,7 @@ public class JarIndex { private Multimap m_innerClasses; private Map m_outerClasses; private Map m_anonymousClasses; - private BiMap m_bridgedMethods; + private Map m_bridgedMethods; public JarIndex() { m_obfClassEntries = Sets.newHashSet(); @@ -79,7 +77,7 @@ public class JarIndex { m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); m_anonymousClasses = Maps.newHashMap(); - m_bridgedMethods = HashBiMap.create(); + m_bridgedMethods = Maps.newHashMap(); } public void indexJar(JarFile jar, boolean buildInnerClasses) { @@ -758,7 +756,7 @@ public class JarIndex { } } - public BiMap getBridgedMethods() { - return m_bridgedMethods; + public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { + return m_bridgedMethods.get(bridgeMethodEntry); } } -- cgit v1.2.3 From 2dc7428e37bdd7a119f53d02ce157675509b0d63 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 23 Feb 2015 23:29:22 -0500 Subject: lots of work in better handling of inner classes also working on recognizing unobfuscated and deobfuscated jars (needed for M3L) --- src/cuchaz/enigma/analysis/JarIndex.java | 67 +++++++++++++++++--------------- 1 file changed, 35 insertions(+), 32 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 1c74f15..6e7c69d 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -61,9 +61,9 @@ public class JarIndex { private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; - private Multimap m_innerClasses; - private Map m_outerClasses; - private Map m_anonymousClasses; + private Multimap m_innerClassesByOuter; + private Map m_outerClassesByInner; + private Map m_anonymousClasses; private Map m_bridgedMethods; public JarIndex() { @@ -74,8 +74,8 @@ public class JarIndex { m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); - m_innerClasses = HashMultimap.create(); - m_outerClasses = Maps.newHashMap(); + m_innerClassesByOuter = HashMultimap.create(); + m_outerClassesByInner = Maps.newHashMap(); m_anonymousClasses = Maps.newHashMap(); m_bridgedMethods = Maps.newHashMap(); } @@ -129,33 +129,40 @@ public class JarIndex { } if (buildInnerClasses) { + // step 5: index inner classes and anonymous classes for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); - String outerClassName = findOuterClass(c); - if (outerClassName != null) { - String innerClassName = c.getSimpleName(); - m_innerClasses.put(outerClassName, innerClassName); - boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; + ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); + ClassEntry outerClassEntry = findOuterClass(c); + if (outerClassEntry != null) { + m_innerClassesByOuter.put(outerClassEntry, innerClassEntry); + boolean innerWasAdded = m_outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; assert (innerWasAdded); - BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); if (enclosingBehavior != null) { - m_anonymousClasses.put(innerClassName, enclosingBehavior); + m_anonymousClasses.put(innerClassEntry, enclosingBehavior); // DEBUG - // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); } else { // DEBUG - // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); } } } // step 6: update other indices with inner class info Map renames = Maps.newHashMap(); - for (Map.Entry entry : m_outerClasses.entrySet()) { - renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); + for (Map.Entry mapEntry : m_innerClassesByOuter.entries()) { + ClassEntry outerClassEntry = mapEntry.getKey(); + ClassEntry innerClassEntry = mapEntry.getValue(); + outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); + String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); + // DEBUG + //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); + renames.put(innerClassEntry.getName(), newName); } EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); m_translationIndex.renameClasses(renames); @@ -290,7 +297,7 @@ public class JarIndex { } } - private String findOuterClass(CtClass c) { + private ClassEntry findOuterClass(CtClass c) { // inner classes: // have constructors that can (illegally) set synthetic fields @@ -341,19 +348,19 @@ public class JarIndex { // do we have an answer yet? if (callerClasses.isEmpty()) { if (illegallySetClasses.size() == 1) { - return illegallySetClasses.iterator().next().getName(); + 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().getName(); + 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().getName(); + return intersection.iterator().next(); } else { System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); } @@ -448,7 +455,7 @@ public class JarIndex { return true; } - private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) { + private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); @@ -669,23 +676,19 @@ public class JarIndex { return behaviorEntries; } - public Collection getInnerClasses(String obfOuterClassName) { - return m_innerClasses.get(obfOuterClassName); + public Collection getInnerClasses(ClassEntry obfOuterClassEntry) { + return m_innerClassesByOuter.get(obfOuterClassEntry); } - public String getOuterClass(String obfInnerClassName) { - // make sure we use the right name - if (new ClassEntry(obfInnerClassName).getPackageName() != null) { - throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); - } - return m_outerClasses.get(obfInnerClassName); + public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { + return m_outerClassesByInner.get(obfInnerClassEntry); } - public boolean isAnonymousClass(String obfInnerClassName) { - return m_anonymousClasses.containsKey(obfInnerClassName); + public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { + return m_anonymousClasses.containsKey(obfInnerClassEntry); } - public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { + public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { return m_anonymousClasses.get(obfInnerClassName); } -- cgit v1.2.3 From 4479acf3df4faf9daac93a396f5bba7cddb0759b Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 25 Feb 2015 00:12:04 -0500 Subject: more work getting inner class trees working in obf'd and deobf'd land --- src/cuchaz/enigma/analysis/JarIndex.java | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 6e7c69d..1afcb76 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -28,6 +28,7 @@ import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; import javassist.bytecode.FieldInfo; +import javassist.bytecode.InnerClassesAttribute; import javassist.expr.ConstructorCall; import javassist.expr.ExprEditor; import javassist.expr.FieldAccess; @@ -160,9 +161,11 @@ public class JarIndex { ClassEntry innerClassEntry = mapEntry.getValue(); outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); - // DEBUG - //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); - renames.put(innerClassEntry.getName(), newName); + if (!innerClassEntry.getName().equals(newName)) { + // DEBUG + //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); + renames.put(innerClassEntry.getName(), newName); + } } EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); m_translationIndex.renameClasses(renames); @@ -299,6 +302,22 @@ public class JarIndex { private ClassEntry findOuterClass(CtClass c) { + ClassEntry classEntry = EntryFactory.getClassEntry(c); + + // does this class already have an outer class? + if (classEntry.isInnerClass()) { + return classEntry.getOuterClassEntry(); + } + InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (innerClassesAttribute != null) { + for (int i=0; i renames = Maps.newHashMap(); - for (Map.Entry mapEntry : m_innerClassesByOuter.entries()) { - ClassEntry outerClassEntry = mapEntry.getKey(); - ClassEntry innerClassEntry = mapEntry.getValue(); - outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); - String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); + for (ClassEntry innerClassEntry : m_innerClassesByOuter.values()) { + String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); if (!innerClassEntry.getName().equals(newName)) { // DEBUG //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); @@ -780,4 +778,25 @@ public class JarIndex { public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { return m_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; + } } -- cgit v1.2.3 From 61eb14f65e73a9b3d0ea6eca6b04da804a4ff61b Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Mar 2015 13:49:44 -0400 Subject: lots of small tweaks and improvements --- src/cuchaz/enigma/analysis/SourceIndex.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index e31b803..b3fb751 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -146,6 +146,10 @@ public class SourceIndex { return m_declarationToToken.values(); } + public Iterable declarations() { + return m_declarationToToken.keySet(); + } + public Token getDeclarationToken(Entry deobfEntry) { return m_declarationToToken.get(deobfEntry); } -- cgit v1.2.3 From 1ad33bfe0a96b1b4a1f3c02cf2c054e8a101dfd8 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Mar 2015 20:08:15 -0400 Subject: field matcher is starting to be useful --- src/cuchaz/enigma/analysis/JarIndex.java | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index e0a8bf5..7ebbd97 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -60,6 +60,8 @@ public class JarIndex { private TranslationIndex m_translationIndex; private Multimap m_interfaces; private Map m_access; + private Multimap m_fields; + private Multimap m_behaviors; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; @@ -73,6 +75,8 @@ public class JarIndex { m_translationIndex = new TranslationIndex(); m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); + m_fields = HashMultimap.create(); + m_behaviors = HashMultimap.create(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); @@ -97,10 +101,14 @@ public class JarIndex { for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); for (CtField field : c.getDeclaredFields()) { - m_access.put(EntryFactory.getFieldEntry(field), Access.get(field)); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); + m_access.put(fieldEntry, Access.get(field)); + m_fields.put(fieldEntry.getClassEntry(), fieldEntry); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - m_access.put(EntryFactory.getBehaviorEntry(behavior), Access.get(behavior)); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + m_access.put(behaviorEntry, Access.get(behavior)); + m_behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); } } @@ -520,6 +528,22 @@ public class JarIndex { return m_obfClassEntries; } + public Collection getObfFieldEntries() { + return m_fields.values(); + } + + public Collection getObfFieldEntries(ClassEntry classEntry) { + return m_fields.get(classEntry); + } + + public Collection getObfBehaviorEntries() { + return m_behaviors.values(); + } + + public Collection getObfBehaviorEntries(ClassEntry classEntry) { + return m_behaviors.get(classEntry); + } + public TranslationIndex getTranslationIndex() { return m_translationIndex; } -- cgit v1.2.3 From 430df87ba5d855ca29bc53a5765a2862d2209098 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Mar 2015 00:55:03 -0400 Subject: tweaks and improvements to field matching gui --- src/cuchaz/enigma/analysis/SourceIndex.java | 8 +++++++- src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java | 10 ++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index b3fb751..8f751ef 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -32,9 +32,15 @@ public class SourceIndex { private Multimap,Token> m_referenceToTokens; private Map m_declarationToToken; private List m_lineOffsets; + private boolean m_ignoreBadTokens; public SourceIndex(String source) { + this(source, true); + } + + public SourceIndex(String source, boolean ignoreBadTokens) { m_source = source; + m_ignoreBadTokens = ignoreBadTokens; m_tokenToReference = Maps.newTreeMap(); m_referenceToTokens = HashMultimap.create(); m_declarationToToken = Maps.newHashMap(); @@ -83,7 +89,7 @@ public class SourceIndex { // 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) { + if (name.lastIndexOf('$') >= 0 && m_ignoreBadTokens) { // DEBUG System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); return null; diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index a9a055b..eb120b6 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -111,10 +111,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { @Override public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); - MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); - ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); - index.addDeclaration(node.getNameToken(), argumentEntry); + if (def.getMethod() instanceof MethodDefinition) { + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); + ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); + index.addDeclaration(node.getNameToken(), argumentEntry); + } return recurse(node, index); } -- cgit v1.2.3 From c133e05b786ff5357931842581571c046f958c74 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 16 Mar 2015 12:29:17 -0400 Subject: fix a zillion issues with inner classes --- src/cuchaz/enigma/analysis/EntryReference.java | 2 +- src/cuchaz/enigma/analysis/JarIndex.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index bb611df..d0a5c6a 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -74,7 +74,7 @@ public class EntryReference { ClassEntry classEntry = (ClassEntry)getNameableEntry(); if (classEntry.isInnerClass()) { // make sure we only rename the inner class name - return classEntry.getInnerClassName(); + return classEntry.getInnermostClassName(); } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 7ebbd97..a4a3abb 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -312,7 +312,7 @@ public class JarIndex { // does this class already have an outer class? if (classEntry.isInnerClass()) { - return classEntry.getOuterClassEntry(); + return classEntry.getOutermostClassEntry(); } InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); if (innerClassesAttribute != null) { -- cgit v1.2.3 From 563c5e08e3d61bfd39402a94e78bbaaf75623b04 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 16 Mar 2015 12:52:09 -0400 Subject: fix more inner class issues --- src/cuchaz/enigma/analysis/JarIndex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index a4a3abb..7ebbd97 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -312,7 +312,7 @@ public class JarIndex { // does this class already have an outer class? if (classEntry.isInnerClass()) { - return classEntry.getOutermostClassEntry(); + return classEntry.getOuterClassEntry(); } InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); if (innerClassesAttribute != null) { -- cgit v1.2.3 From 5e3743a0aca3529eacf9be400c8b8d7547f66e7f Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 16 Mar 2015 19:22:22 -0400 Subject: started adding minimal support for generics fixed mark-as-deobfuscated issue --- src/cuchaz/enigma/analysis/JarIndex.java | 31 +++++++++++++++------- .../enigma/analysis/SourceIndexClassVisitor.java | 7 ++--- 2 files changed, 24 insertions(+), 14 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 7ebbd97..e255468 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -28,6 +28,7 @@ import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; +import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.FieldInfo; import javassist.bytecode.InnerClassesAttribute; import javassist.expr.ConstructorCall; @@ -314,15 +315,6 @@ public class JarIndex { if (classEntry.isInnerClass()) { return classEntry.getOuterClassEntry(); } - InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (innerClassesAttribute != null) { - for (int i=0; i 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: diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index f4f4956..f4202b5 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -30,7 +30,6 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.Type; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -93,8 +92,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -106,8 +104,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); -- cgit v1.2.3 From b7c91d19660ef4a36b0ad3edae81575fb57c39fe Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 20 Mar 2015 12:40:07 -0400 Subject: get rid of stupid fucking com.beust.jcommander imports crashing all my downstream apps Eclipse loves to import that particular collections implementation for some reason and I don't want to use it!! --- src/cuchaz/enigma/analysis/RelatedMethodChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java index 5bd67a0..e231b80 100644 --- a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java +++ b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java @@ -3,7 +3,7 @@ package cuchaz.enigma.analysis; import java.util.Map; import java.util.Set; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.mapping.BehaviorEntry; -- cgit v1.2.3 From 698f4d025e0b9ee5bbd1bd701cc46c340b8b4d9c Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sat, 28 Mar 2015 19:12:53 -0400 Subject: add methods for better runtime obfuscation in M3L --- src/cuchaz/enigma/analysis/TranslationIndex.java | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 8651ebd..bd77344 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -84,6 +84,10 @@ public class TranslationIndex implements Serializable { } public void indexClass(CtClass c) { + indexClass(c, true); + } + + public void indexClass(CtClass c, boolean indexMembers) { ClassEntry classEntry = EntryFactory.getClassEntry(c); @@ -93,16 +97,18 @@ public class TranslationIndex implements Serializable { m_superclasses.put(classEntry, superclassEntry); } - // add fields - for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); - m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); - } - - // add behaviors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + if (indexMembers) { + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); + m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } } } -- cgit v1.2.3 From 3b57f50a1d9429966e7aced0b81b3b3d4cfa41c7 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Mon, 30 Mar 2015 11:40:24 -0400 Subject: fix unintentional compile time transitive dependency on procyon --- src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java | 4 ++-- src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index eb120b6..3a176ff 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -31,9 +31,9 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.ProcyonEntryFactory; import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Type; @@ -113,7 +113,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); if (def.getMethod() instanceof MethodDefinition) { MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); + BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(methodDef); ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); index.addDeclaration(node.getNameToken(), argumentEntry); } diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index f4202b5..37a893d 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -28,8 +28,8 @@ import com.strobel.decompiler.languages.java.ast.VariableInitializer; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.ProcyonEntryFactory; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -67,7 +67,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(def); + BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); AstNode tokenNode = node.getNameToken(); if (behaviorEntry instanceof ConstructorEntry) { @@ -84,7 +84,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(def); + ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); index.addDeclaration(node.getNameToken(), constructorEntry); return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); } @@ -92,7 +92,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); + FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -104,7 +104,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); + FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); -- cgit v1.2.3 From 2fc8ae770442ec3ab91cf0c16cc30917e0d048d3 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Mon, 30 Mar 2015 23:56:21 -0400 Subject: resolve methods using interfaces as well as superclasses --- src/cuchaz/enigma/analysis/JarIndex.java | 36 ++++++------- src/cuchaz/enigma/analysis/TranslationIndex.java | 69 +++++++++++++++++++++++- 2 files changed, 84 insertions(+), 21 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index e255468..6b3cf67 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -59,7 +59,6 @@ public class JarIndex { private Set m_obfClassEntries; private TranslationIndex m_translationIndex; - private Multimap m_interfaces; private Map m_access; private Multimap m_fields; private Multimap m_behaviors; @@ -74,7 +73,6 @@ public class JarIndex { public JarIndex() { m_obfClassEntries = Sets.newHashSet(); m_translationIndex = new TranslationIndex(); - m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); m_fields = HashMultimap.create(); m_behaviors = HashMultimap.create(); @@ -124,7 +122,6 @@ public class JarIndex { if (className.equals(interfaceName)) { throw new IllegalArgumentException("Class cannot be its own interface! " + className); } - m_interfaces.put(className, interfaceName); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { indexBehavior(behavior); @@ -176,7 +173,6 @@ public class JarIndex { } EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); m_translationIndex.renameClasses(renames); - EntryRenamer.renameClassesInMultimap(renames, m_interfaces); EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); @@ -637,11 +633,11 @@ public class JarIndex { interfaceMethodEntries.add(obfMethodEntry); } else { // get the interface class - for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( - new ClassEntry(interfaceName), + interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getSignature() ); @@ -745,31 +741,33 @@ public class JarIndex { return m_anonymousClasses.get(obfInnerClassName); } - public Set getInterfaces(String className) { - Set interfaceNames = new HashSet(); - interfaceNames.addAll(m_interfaces.get(className)); - for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) { - interfaceNames.addAll(m_interfaces.get(ancestor.getName())); + public Set getInterfaces(String className) { + ClassEntry classEntry = new ClassEntry(className); + Set interfaces = new HashSet(); + interfaces.addAll(m_translationIndex.getInterfaces(classEntry)); + for (ClassEntry ancestor : m_translationIndex.getAncestry(classEntry)) { + interfaces.addAll(m_translationIndex.getInterfaces(ancestor)); } - return interfaceNames; + return interfaces; } public Set getImplementingClasses(String targetInterfaceName) { + // linear search is fast enough for now Set classNames = Sets.newHashSet(); - for (Map.Entry entry : m_interfaces.entries()) { - String className = entry.getKey(); - String interfaceName = entry.getValue(); - if (interfaceName.equals(targetInterfaceName)) { - classNames.add(className); - m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className)); + for (Map.Entry entry : m_translationIndex.getClassInterfaces()) { + ClassEntry classEntry = entry.getKey(); + ClassEntry interfaceEntry = entry.getValue(); + if (interfaceEntry.getName().equals(targetInterfaceName)) { + classNames.add(classEntry.getClassName()); + m_translationIndex.getSubclassNamesRecursively(classNames, classEntry); } } return classNames; } public boolean isInterface(String className) { - return m_interfaces.containsValue(className); + return m_translationIndex.isInterface(new ClassEntry(className)); } public boolean containsObfClass(ClassEntry obfClassEntry) { diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index bd77344..e0e66bf 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -16,6 +16,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,6 +27,7 @@ import java.util.zip.GZIPOutputStream; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtField; +import javassist.bytecode.Descriptor; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; @@ -36,8 +38,8 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Translator; public class TranslationIndex implements Serializable { @@ -47,11 +49,13 @@ public class TranslationIndex implements Serializable { private Map m_superclasses; private Multimap m_fieldEntries; private Multimap m_behaviorEntries; + private Multimap m_interfaces; public TranslationIndex() { m_superclasses = Maps.newHashMap(); m_fieldEntries = HashMultimap.create(); m_behaviorEntries = HashMultimap.create(); + m_interfaces = HashMultimap.create(); } public TranslationIndex(TranslationIndex other, Translator translator) { @@ -65,6 +69,15 @@ public class TranslationIndex implements Serializable { ); } + // translate the interfaces + m_interfaces = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_interfaces.entries()) { + m_interfaces.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + // translate the fields m_fieldEntries = HashMultimap.create(); for (Map.Entry mapEntry : other.m_fieldEntries.entries()) { @@ -90,13 +103,24 @@ public class TranslationIndex implements Serializable { 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 (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { + if (superclassEntry != null && !isJre(superclassEntry)) { m_superclasses.put(classEntry, superclassEntry); } + // add the interfaces + for (String interfaceClassName : c.getClassFile().getInterfaces()) { + ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); + if (!isJre(interfaceClassEntry)) { + m_interfaces.put(classEntry, interfaceClassEntry); + } + } + if (indexMembers) { // add fields for (CtField field : c.getDeclaredFields()) { @@ -134,6 +158,7 @@ public class TranslationIndex implements Serializable { } public List getSubclass(ClassEntry classEntry) { + // linear search is fast enough for now List subclasses = Lists.newArrayList(); for (Map.Entry entry : m_superclasses.entrySet()) { @@ -160,6 +185,18 @@ public class TranslationIndex implements Serializable { } } + public Collection> getClassInterfaces() { + return m_interfaces.entries(); + } + + public Collection getInterfaces(ClassEntry classEntry) { + return m_interfaces.get(classEntry); + } + + public boolean isInterface(ClassEntry classEntry) { + return m_interfaces.containsValue(classEntry); + } + public boolean entryExists(Entry entry) { if (entry instanceof FieldEntry) { return fieldExists((FieldEntry)entry); @@ -185,6 +222,21 @@ public class TranslationIndex implements Serializable { return (ClassEntry)entry; } + ClassEntry superclassEntry = resolveSuperclass(entry); + if (superclassEntry != null) { + return superclassEntry; + } + + ClassEntry interfaceEntry = resolveInterface(entry); + if (interfaceEntry != null) { + return interfaceEntry; + } + + return null; + } + + 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)) { @@ -203,6 +255,19 @@ public class TranslationIndex implements Serializable { 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 : m_interfaces.get(entry.getClassEntry())) { + 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")); -- cgit v1.2.3 From 808a855be801d101790e370e6fe0368117aba15d Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sat, 11 Apr 2015 14:06:04 -0400 Subject: save more translation information for m3l --- src/cuchaz/enigma/analysis/TranslationIndex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index e0e66bf..adfb3fa 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -109,7 +109,7 @@ public class TranslationIndex implements Serializable { // add the superclass ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); - if (superclassEntry != null && !isJre(superclassEntry)) { + if (superclassEntry != null) { m_superclasses.put(classEntry, superclassEntry); } -- cgit v1.2.3 From 51b92b780b57cc955e0618d09fbc3aa95ff47163 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sun, 19 Apr 2015 18:51:04 -0400 Subject: relicense Enigma as LGPL --- src/cuchaz/enigma/analysis/Access.java | 8 ++++---- src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java | 8 ++++---- src/cuchaz/enigma/analysis/BridgeMarker.java | 10 ++++++++++ src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java | 8 ++++---- src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java | 8 ++++---- src/cuchaz/enigma/analysis/EntryReference.java | 8 ++++---- src/cuchaz/enigma/analysis/EntryRenamer.java | 8 ++++---- src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java | 8 ++++---- src/cuchaz/enigma/analysis/JarClassIterator.java | 8 ++++---- src/cuchaz/enigma/analysis/JarIndex.java | 8 ++++---- src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java | 8 ++++---- src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java | 8 ++++---- src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 8 ++++---- src/cuchaz/enigma/analysis/RelatedMethodChecker.java | 10 ++++++++++ src/cuchaz/enigma/analysis/SourceIndex.java | 8 ++++---- src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java | 8 ++++---- src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | 8 ++++---- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 8 ++++---- src/cuchaz/enigma/analysis/Token.java | 8 ++++---- src/cuchaz/enigma/analysis/TranslationIndex.java | 8 ++++---- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 8 ++++---- 21 files changed, 96 insertions(+), 76 deletions(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java index 8d3409a..1c8cfc4 100644 --- a/src/cuchaz/enigma/analysis/Access.java +++ b/src/cuchaz/enigma/analysis/Access.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 9adac5e..353a4bf 100644 --- a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/BridgeMarker.java b/src/cuchaz/enigma/analysis/BridgeMarker.java index 28e3517..650b3a7 100644 --- a/src/cuchaz/enigma/analysis/BridgeMarker.java +++ b/src/cuchaz/enigma/analysis/BridgeMarker.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.analysis; import javassist.CtClass; diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 49aac5f..cc70f51 100644 --- a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 3eaa391..7542bd9 100644 --- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index d0a5c6a..8512723 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index 2f27049..f748274 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 2173eea..4ed8fee 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index 72a9912..aa58e9e 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 6b3cf67..5c8ec1c 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 6cafc55..aa0aeca 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index 8718220..0da3c8c 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java index 2b08616..4d81bf1 100644 --- a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java index e231b80..e592a1c 100644 --- a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java +++ b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.analysis; import java.util.Map; diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 8f751ef..3c4ac46 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index 3a176ff..a660a37 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 37a893d..db0bc0b 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 0d5bdc0..0869826 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java index 481d2f4..76d6327 100644 --- a/src/cuchaz/enigma/analysis/Token.java +++ b/src/cuchaz/enigma/analysis/Token.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index adfb3fa..a491cfc 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index 23f8089..0a90bac 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation -- cgit v1.2.3 From c7a3de184dc51526ee157d47981a74ffba284f5d Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sun, 24 May 2015 11:23:36 -0400 Subject: fix broken tests, and one broken function. =) --- src/cuchaz/enigma/analysis/JarIndex.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 5c8ec1c..7e3c1b5 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -567,7 +567,9 @@ public class JarIndex { List ancestry = Lists.newArrayList(); ancestry.add(obfClassEntry.getName()); for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) { - ancestry.add(classEntry.getName()); + if (containsObfClass(classEntry)) { + ancestry.add(classEntry.getName()); + } } ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, -- cgit v1.2.3