summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/Ancestries.java
diff options
context:
space:
mode:
authorGravatar jeff2014-08-11 00:02:00 -0400
committerGravatar jeff2014-08-11 00:02:00 -0400
commitbba7c6a19c15bc82946176c79a4eba3612b25f17 (patch)
treef0b55befaa6e7e532e9728dfa6b1c9cb36660594 /src/cuchaz/enigma/analysis/Ancestries.java
parentadded backwards navigation (diff)
downloadenigma-fork-bba7c6a19c15bc82946176c79a4eba3612b25f17.tar.gz
enigma-fork-bba7c6a19c15bc82946176c79a4eba3612b25f17.tar.xz
enigma-fork-bba7c6a19c15bc82946176c79a4eba3612b25f17.zip
added method inheritance browsing
also finally fixed method renamer to rename all method implementations in the inheritance hierarchy.
Diffstat (limited to 'src/cuchaz/enigma/analysis/Ancestries.java')
-rw-r--r--src/cuchaz/enigma/analysis/Ancestries.java235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java
new file mode 100644
index 0000000..e6c8bbf
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/Ancestries.java
@@ -0,0 +1,235 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.Serializable;
17import java.util.ArrayList;
18import java.util.Collection;
19import java.util.List;
20import java.util.Map;
21import java.util.zip.ZipEntry;
22import java.util.zip.ZipInputStream;
23
24import javassist.ByteArrayClassPath;
25import javassist.ClassPool;
26import javassist.CtClass;
27import javassist.NotFoundException;
28import javassist.bytecode.Descriptor;
29import javassist.bytecode.MethodInfo;
30
31import com.google.common.collect.HashMultimap;
32import com.google.common.collect.Lists;
33import com.google.common.collect.Maps;
34import com.google.common.collect.Multimap;
35
36import cuchaz.enigma.Constants;
37import cuchaz.enigma.mapping.ClassEntry;
38import cuchaz.enigma.mapping.MethodEntry;
39import cuchaz.enigma.mapping.Translator;
40
41public class Ancestries implements Serializable
42{
43 private static final long serialVersionUID = 738687982126844179L;
44
45 private Map<String,String> m_superclasses;
46 private Multimap<String,String> m_methodImplementations;
47
48 public Ancestries( )
49 {
50 m_superclasses = Maps.newHashMap();
51 m_methodImplementations = HashMultimap.create();
52 }
53
54 @SuppressWarnings( "unchecked" )
55 public void readFromJar( InputStream in )
56 throws IOException
57 {
58 ClassPool classPool = new ClassPool();
59
60 ZipInputStream zin = new ZipInputStream( in );
61 ZipEntry entry;
62 while( ( entry = zin.getNextEntry() ) != null )
63 {
64 // filter out non-classes
65 if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) )
66 {
67 continue;
68 }
69
70 // read the class into a buffer
71 ByteArrayOutputStream bos = new ByteArrayOutputStream();
72 byte[] buf = new byte[Constants.KiB];
73 int totalNumBytesRead = 0;
74 while( zin.available() > 0 )
75 {
76 int numBytesRead = zin.read( buf );
77 if( numBytesRead < 0 )
78 {
79 break;
80 }
81 bos.write( buf, 0, numBytesRead );
82
83 // sanity checking
84 totalNumBytesRead += numBytesRead;
85 if( totalNumBytesRead > Constants.MiB )
86 {
87 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
88 }
89 }
90
91 // determine the class name (ie chop off the ".class")
92 String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
93
94 // get a javassist handle for the class
95 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
96 try
97 {
98 CtClass c = classPool.get( className );
99 addSuperclass( c.getName(), c.getClassFile().getSuperclass() );
100 addMethodImplementations( c.getName(), (List<MethodInfo>)c.getClassFile().getMethods() );
101 }
102 catch( NotFoundException ex )
103 {
104 throw new Error( "Unable to load class: " + className );
105 }
106 }
107 }
108
109 private void addMethodImplementations( String name, List<MethodInfo> methods )
110 {
111 for( MethodInfo method : methods )
112 {
113 m_methodImplementations.put( name, getMethodKey( method.getName(), method.getDescriptor() ) );
114 }
115 }
116
117 public void addSuperclass( String className, String superclassName )
118 {
119 className = Descriptor.toJvmName( className );
120 superclassName = Descriptor.toJvmName( superclassName );
121
122 if( className.equals( superclassName ) )
123 {
124 throw new IllegalArgumentException( "Class cannot be its own superclass! " + className );
125 }
126
127 if( !isJre( className ) && !isJre( superclassName ) )
128 {
129 m_superclasses.put( className, superclassName );
130 }
131 }
132
133 public String getSuperclassName( String className )
134 {
135 return m_superclasses.get( className );
136 }
137
138 public List<String> getAncestry( String className )
139 {
140 List<String> ancestors = new ArrayList<String>();
141 while( className != null )
142 {
143 className = getSuperclassName( className );
144 if( className != null )
145 {
146 ancestors.add( className );
147 }
148 }
149 return ancestors;
150 }
151
152 public List<String> getSubclasses( String className )
153 {
154 // linear search is fast enough for now
155 List<String> subclasses = Lists.newArrayList();
156 for( Map.Entry<String,String> entry : m_superclasses.entrySet() )
157 {
158 String subclass = entry.getKey();
159 String superclass = entry.getValue();
160 if( className.equals( superclass ) )
161 {
162 subclasses.add( subclass );
163 }
164 }
165 return subclasses;
166 }
167
168 public boolean isMethodImplemented( MethodEntry methodEntry )
169 {
170 return isMethodImplemented( methodEntry.getClassName(), methodEntry.getName(), methodEntry.getSignature() );
171 }
172
173 public boolean isMethodImplemented( String className, String methodName, String methodSignature )
174 {
175 Collection<String> implementations = m_methodImplementations.get( className );
176 if( implementations == null )
177 {
178 return false;
179 }
180 return implementations.contains( getMethodKey( methodName, methodSignature ) );
181 }
182
183 public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry )
184 {
185 // get the root node
186 List<String> ancestry = getAncestry( obfClassEntry.getName() );
187 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) );
188
189 // expand all children recursively
190 rootNode.load( this, true );
191
192 return rootNode;
193 }
194
195 public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry )
196 {
197 // travel to the ancestor implementation
198 String baseImplementationClassName = obfMethodEntry.getClassName();
199 for( String ancestorClassName : getAncestry( obfMethodEntry.getClassName() ) )
200 {
201 if( isMethodImplemented( ancestorClassName, obfMethodEntry.getName(), obfMethodEntry.getSignature() ) )
202 {
203 baseImplementationClassName = ancestorClassName;
204 }
205 }
206
207 // make a root node at the base
208 MethodEntry methodEntry = new MethodEntry(
209 new ClassEntry( baseImplementationClassName ),
210 obfMethodEntry.getName(),
211 obfMethodEntry.getSignature()
212 );
213 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
214 deobfuscatingTranslator,
215 methodEntry,
216 isMethodImplemented( methodEntry )
217 );
218
219 // expand the full tree
220 rootNode.load( this, true );
221
222 return rootNode;
223 }
224
225 private boolean isJre( String className )
226 {
227 return className.startsWith( "java/" )
228 || className.startsWith( "javax/" );
229 }
230
231 private String getMethodKey( String name, String signature )
232 {
233 return name + signature;
234 }
235}